 //Aibote
 //官网：http://www.aibote.net/
 //aibote协议文档
 //http://www.aibote.net/aiboteProtocol.html
 //Aibote是国人自主研发的一款RPA办公自动化框架。作为一款国人自主研发的自动化框架 ，从专业角度来说，比国外开源框架功能更为强大、灵活，使用方便。
 //Aibote支持Windows+Web+Android(含设备投屏)三大主流平台办公自动化需求
 //1、Windows办公自动化，支持Excel、数据库、邮件收发。UI元素定位技术，急速稳定，零误差。
 //2、Web办公自动化，内置Chrome内核最高权限，直接操作v8驱动浏览器速度非常之快。
 //3、Android办公自动化，一键安装Android精灵环境，支持Android 4-10系统所有版本，无需root，支持群控多个模拟器和安卓真机。
 //4、Android设备投屏，鼠标键盘控制Android设备，一键安装上传应用。一人轻松操作上百台手机同时办公

 //Aibote可以同时协作Windows+Web+Android机器人办公，满足您多样化需求。
 //本单元 封装Aibote Pascal调用方法  有需要成品或者定制可以+q详聊
 //版本：V1.0
 //时间：2023-2-7
 //作者：黑魔(QQ:274838061)
 //开源地址：https://gitee.com/heimo88/aibote4-pascal
 //Aibote需要的 驱动程序请自行到官网下载 http://www.aibote.net/download.html
unit hmAibote;

{$mode Delphi} {$H+}
{$modeswitch functionreferences}{$modeswitch anonymousfunctions}



interface

uses
  Classes, blcksock, synsock, SysUtils, process, Dialogs, Forms, Windows, JwaTlHelp32, DateUtils, fpjson, jsonparser;

type


  { TWebBot }
  {$region TWebBotCls}
  //WebBot Pascal接口By 黑魔(QQ:274838061)
  TWebBot = class(TThread)
  private

  public
    ParentForm: TForm;
    Sock: TTCPBlockSocket;
    ClientSock: TTCPBlockSocket;
    Proc: tprocess;

    intervalTimeout, waitTimeout: integer;

    IP:   string;
    Port: integer;
    browserName: string;
    userDataDir: string;
    browserPath: string;
    argument: string;
    Driverpath: string;
    ///**
    //* @param pform 主窗口
    //* @param  ip 脚本所在的地址，传递给WebDriver.exe。如果值为 "127.0.0.1"脚本会将参数 ip和port作为启动参数并启动WebDriver.exe，否则用户需要手动启动WebDriver.exe 并且提供启动参数。
    //* @param  port 监听端口，传递给WebDriver.exe
    //* @param {{browserName:string, debugPort:number, userDataDir:string, browserPath:string, argument:string}} options 可选参数
    //    * browserName 浏览器名称，默认 chrome 浏览器。除edge和chrome浏览器会自动寻找浏览器路径，其他浏览器需要指定browserPath。
    //    * debugPort 调试端口,默认 0 随机端口。指定端口则接管已打开的浏览器。启动浏览应指定的参数 --remote-debugging-port=19222 --user-data-dir=C:\\Users\\电脑用户名\\AppData\\Local\\Google\\Chrome\\User Data
    //    * userDataDir 用户数据目录,默认./UserData。多进程同时操作多个浏览器数据目录不能相同
    //    * browserPath 浏览器路径
    //    * argument 浏览器启动参数。例如：无头模式: --headless   设置代理：--proxy-server=127.0.0.1:8080
    //*/
    constructor Create(pform: TForm; ip: string; port: integer; driverpath: string = 'WebDriver.exe'; browserName: string = 'edge'; userDataDir: string = './UserData'; browserPath: string = 'null'; argument: string = 'null');
    destructor Destroy; override;
    procedure Execute; override;
    procedure RunDriver;
    procedure CheckAndKillChildProcess;
    //是否已经连接
    function IsConnected: boolean;

    function setSendData(Args: array of string): string;
    function sendData(strData: string): string;

    function sendDataWaitTrue(strData: string; waitret: string = 'false'): string;

    //转到指定URL
    function gotoUrl(const url: string): boolean;
    //新建tab页面并跳转到指定url
    function newPage(const url: string): boolean;
    //返回
    function back: boolean;
    //前进
    function forward: boolean;
    //刷新
    function refresh: boolean;
    //获取当前页面ID
    function getCurPageId: string;
    //获取所有页面ID
    function getAllPageId: Tarray<string>;
    // 切换指定页面
    function switchPage(const pageId: string): boolean;
    //关闭当前页面
    function closePage: boolean;
    //获取当前页面URL
    function getCurrentUrl: string;
    //获取当前页面标题
    function getTitle: string;
    //切换frame
    function switchFrame(const xpath: string): boolean;
    //切换到主frame
    function switchMainFrame: boolean;
    //点击元素
    function clickElement(const xpath: string): boolean;
    //设置编辑框值
    function setElementValue(const xpath, Value: string): boolean;
    //获取文本
    function getElementText(const xpath: string): string;
    //获取outerHTML
    function getElementOuterHTML(const xpath: string): string;
    //获取innerHTML
    function getElementInnerHTML(const xpath: string): string;
    //设置属性值
    function setElementAttribute(const xpath, Name, Value: string): boolean;

    //获取指定属性的值
    function getElementAttribute(const xpath, Name: string): string;
    //获取矩形位置
    function getElementRect(const xpath: string): string;

    //判断元素是否选中
    function isSelected(const xpath: string): boolean;
    //判断元素是否可见
    function isDisplayed(const xpath: string): boolean;
    //判断元素是否可用
    function isEnabled(const xpath: string): boolean;
    //清空元素值
    function clearElement(const xpath: string): boolean;
    //设置元素焦点
    function setElementFocus(const xpath: string): boolean;
    //通过元素上传文件
    function uploadFile(const xpath, filePath: string): boolean;
    //输入文本
    function sendKeys(const xpath, Value: string): boolean;
    //发送Vk虚拟键
    //VK键值，仅支持 回退键:8  制表键:9  回车键:13  空格键:32  方向左键:37  方向上键:38  方向右键:39  方向下键:40  删除键:46
    function sendVk(const vkCode: string): boolean;
    //点击鼠标
    //x 横坐标，非Windows坐标，页面左上角为起始坐标
    //y 纵坐标，非Windows坐标，页面左上角为起始坐标
    //msg 单击左键:1  单击右键:2  按下左键:3  弹起左键:4  按下右键:5  弹起右键:6  双击左键：7
    function clickMouse(x, y, msg: integer): boolean;
    //移动鼠标
    //x 横坐标，非Windows坐标，页面左上角为起始坐标
    //y 纵坐标，非Windows坐标，页面左上角为起始坐标
    function moveMouse(x, y: integer): boolean;
    //滚动鼠标
    //deltaX 水平滚动条移动的距离
    //deltaY 垂直滚动条移动的距离
    //x 可选参数，鼠标横坐标位置， 默认为0
    //y 可选参数，鼠标纵坐标位置， 默认为0
    function wheelMouse(deltaX, deltaY: integer; x: integer = 0; y: integer = 0): boolean;
    //通过xpath 点击鼠标
    //msg 单击左键:1  单击右键:2  按下左键:3  弹起左键:4  按下右键:5  弹起右键:6  双击左键：7
    function clickMouseByXpath(const xpath: string; msg: integer): boolean;
    //通过xpath 移动鼠标
    function moveMouseByXpath(const xpath: string): boolean;
    //通过xpath 滚动鼠标
    //deltaX 水平滚动条移动的距离
    //deltaY 垂直滚动条移动的距离
    function wheelMouseByXpath(const xpath: string; deltaX, deltaY: integer): boolean;
    //截图
    //xpath 可选参数，元素路径。如果指定该参数则截取元素图片
    function takeScreenshot(xpath: string = ''): string;
    //点击警告框
    //acceptOrCancel true接受, false取消
    //promptText 可选参数，输入prompt警告框文本
    function clickAlert(const acceptOrCancel: string; promptText: string = ''): boolean;
    //获取警告框内容
    function getAlertText: string;
    //获取指定url匹配的cookies
    //url 指定的url http://或https:// 起头
    function getCookies(const url: string): string;
    //获取所有cookies
    function getAllCookies: string;
    //设置cookie
    //cookie参数， name、value和url必填参数，其他参数可选
    function setCookie(const Name, Value, url: string; domain: string = ''; path: string = ''; secure: string = 'false'; httpOnly: string = 'false'; sameSite: string = ''; expires: string = '0';
      priority: string = ''; sameParty: string = 'false'; sourceScheme: string = ''; sourcePort: string = '0'; partitionKey: string = ''): boolean;
    //删除指定cookies
    //url 如果指定，则删除所有匹配 url 和 name的Cookie
    // domain 如果指定，则删除所有匹配 domain 和 name的Cookie
    // path 如果指定，则删除所有匹配 path 和 name的Cookie
    function deleteCookies(const Name: string; url: string = ''; domain: string = ''; path: string = ''): boolean;
    //删除所有cookies
    function deleteAllCookies: boolean;
    //清除缓存
    function clearCache: boolean;
    //执行JavaScript
    function executeScript(const command: string): string;
    //获取窗口位置和状态
    function getWindowPos: string;
    //设置窗口位置和状态
    //windowState 窗口状态，正常:"normal"  最小化:"minimized"  最大化:"maximized"  全屏:"fullscreen"
    //left:0, top:0, width:0, height:0可选参数，浏览器窗口位置，此参数仅windowState 值为 "normal" 时有效
    function setWindowPos(const windowState: string; left: integer = 0; top: integer = 0; Width: integer = 0; Height: integer = 0): boolean;
  end;

  {$endregion}

  { TWindowsBot }
  {$region TWindowsBotCls}

  //WindowsBot Pascal接口By 黑魔(QQ:274838061)
  TWindowsBot = class(TThread)


  public
    ParentForm: TForm;
    Sock: TTCPBlockSocket;
    ClientSock: TTCPBlockSocket;
    Proc: tprocess;

    intervalTimeout, waitTimeout: integer;

    IP:   string;
    Port: integer;
    Driverpath: string;

    constructor Create(pform: TForm; ip: string; port: integer; driverpath: string = 'WindowsDriver.exe');
    destructor Destroy; override;
    procedure Execute; override;
    procedure RunDriver;

    //是否已经连接
    function IsConnected: boolean;

    function setSendData(Args: array of string): string;
    function sendData(strData: string): string;

    function sendDataWaitTrue(strData: string; waitret: string = 'false'): string;

    ///**设置隐式等待
    // * @param {number} waitMs  等待时间,单位毫秒
    // * @param {number} intervalMs 心跳间隔，单位毫秒。可选参数，默认10毫秒
    // * @return {Promise.<void>}
    //*/
    procedure setImplicitTimeout(waitMs: integer; intervalMs: integer = 10);
    ///**查找窗口句柄
    // * @param {string} className  窗口类名
    // * @param {string} windowNmae 窗口名
    // * @return {Promise.<string>} 成功返回窗口句柄，失败返回null
    //*/
    function findWindow(const ClassName, windowName: string): string;
    ///**查找窗口句柄数组
    // * @param {string} className  窗口类名
    // * @param {string} windowNmae 窗口名
    // * @return {Promise.<[]>} 成功返回窗口句柄数组，失败返回[]
    //*/
    function findWindows(const ClassName, windowName: string): tarray<string>;
    ///**查找子窗口句柄
    // * @param {string|number} curHwnd  当前窗口句柄
    // * @param {string} className  窗口类名
    // * @param {string} windowNmae 窗口名
    // * @return {Promise.<string>} 成功返回窗口句柄，失败返回null
    //*/
    function findSubWindow(const curHwnd, ClassName, windowName: string): string;
    ///**查找父窗口句柄
    // * @param {string|number} curHwnd  当前窗口句柄
    // * @return {Promise.<string>} 成功返回窗口句柄，失败返回null
    //*/
    function findParentWindow(const curHwnd: string): string;
    ///**查找桌面窗口句柄
    // * @return {Promise.<string>} 成功返回窗口句柄，失败返回null
    //*/
    function findDesktopWindow: string;
    ///**获取窗口名称
    // * @param {string|number} hwnd 窗口句柄
    // * @return {Promise.<string>} 成功返回窗口名称，失败返回null
    //*/
    function getWindowName(const hwnd: string): string;
    // /**显示/隐藏窗口
    // * @param {string|number} hwnd 窗口句柄
    // * @param {boolean} isShow 显示窗口 true， 隐藏窗口 false
    // * @return {Promise.<boolean>} 成功返回true，失败返回false
    //*/
    function showWindow(const hwnd, isShow: string): boolean;
    // /**设置窗口到最顶层
    // * @param {string|number} hwnd 窗口句柄
    // * @param {boolean} isTop 是否置顶，true置顶， false取消置顶
    // * @return {Promise.<boolean>} 成功返回true，失败返回false
    //*/
    function setWindowTop(const hwnd, isTop: string): boolean;
    ///**获取窗口位置
    // * @param {string|number} hwnd  窗口句柄
    // * @return 成功返回窗口位置，失败返回-1|-1|-1|-1
    //*/
    function getWindowPos(const hwnd: string): string;
    ///**设置窗口位置
    // * @param {string|number} hwnd  窗口句柄
    // * @param {number} left 左上角横坐标
    // * @param {number} top 左上角纵坐标
    // * @param {number} width 窗口宽度
    // * @param {number} height 窗口高度
    // * @return {Promise.<boolean>} 成功返回true 失败返回 false
    //*/
    function setWindowPos(const hwnd, left, top, Width, Height: string): boolean;
    ///**移动鼠标
    // * @param {string|number} hwnd  窗口句柄
    // * @param {number} x  横坐标
    // * @param {number} y  纵坐标
    // * @param {{mode:boolean, elementHwnd:string|number}}操作模式，后台 true，前台 false。默认前台操作。
    // * 如果mode值为true且目标控件有单独的句柄，则需要通过getElementWindow获得元素句柄，指定elementHwnd的值(极少应用窗口由父窗口响应消息，则无需指定)
    //*/
    procedure moveMouse(const hwnd, x, y: string; mode: string = 'false'; elementHwnd: string = '0');

    ///**移动鼠标(相对坐标)
    // * @param {string|number} hwnd  窗口句柄
    // * @param {number} x  相对横坐标
    // * @param {number} y  相对纵坐标
    // * @param {boolean} mode  操作模式，后台 true，前台 false。默认前台操作
    //*/
    procedure moveMouseRelative(const hwnd, x, y: string; mode: string = 'false');
    ///**滚动鼠标
    // * @param {string|number} hwnd  窗口句柄
    // * @param {number} x  横坐标
    // * @param {number} y  纵坐标
    // * @param {number} dwData 鼠标滚动次数,负数下滚鼠标,正数上滚鼠标
    // * @param {boolean} mode  操作模式，后台 true，前台 false。默认前台操作
    //*/
    procedure rollMouse(const hwnd, x, y: string; dwData: string = '1'; mode: string = 'false');
    ///**鼠标点击
    // * @param {string|number} hwnd  窗口句柄
    // * @param {number} x  横坐标
    // * @param {number} y  纵坐标
    // * @param {number} msg 单击左键:1 单击右键:2 按下左键:3 弹起左键:4 按下右键:5 弹起右键:6 双击左键:7 双击右键:8
    // * @param {{mode:boolean, elementHwnd:string|number}} 操作模式，后台 true，前台 false。默认前台操作。
    // * 如果mode值为true且目标控件有单独的句柄，则需要通过getElementWindow获得元素句柄，指定elementHwnd的值(极少应用窗口由父窗口响应消息，则无需指定)
    //*/
    procedure clickMouse(const hwnd, x, y: string; msg: string = '1'; mode: string = 'false'; elementHwnd: string = '0');
    ///**输入文本
    // * @param {string} text  输入的文本
    //*/
    procedure sendKeys(const Text: string);
    ///**输入虚拟键值(VK)
    //  * @param {number} bVk VK键值，例如：回车对应 VK键值 13
    //  * @param {number} msg 按下弹起:1 按下:2 弹起:3
    // */
    procedure sendVk(const bVk: string; msg: string = '1');
    ///**后台输入虚拟键值(VK)
    // * @param {string|number} hwnd  窗口句柄，如果目标控件有单独的句柄，需要通过getElementWindow获得句柄
    // * @param {number} bVk VK键值，例如：回车对应 VK键值 13
    // * @param {number} msg 按下弹起:1 按下:2 弹起:3
    // * @return 若是后台组合键，可使用sendVk 按下控制键(Alt、Shift、Ctrl...)，再组合其他按键
    //*/
    procedure sendVkByHwnd(const hwnd, bVk: string; msg: string = '1');
    ///**截图保存
    // * @param {string|number} hwnd  窗口句柄
    // * @param {string} savePath 保存的位置
    // * @param {{region:[left:number, top:number, right:number, bottom:number], threshold:[thresholdType:number, thresh:number, maxval:number], mode:boolean}} options 可选参数
    // * region截图区域 [10, 20, 100, 200]，region默认  hwnd对应的窗口
    // * threshold二值化图片, thresholdType算法类型：
    //        0   THRESH_BINARY算法，当前点值大于阈值thresh时，取最大值maxva，否则设置为0
    //        1   THRESH_BINARY_INV算法，当前点值大于阈值thresh时，设置为0，否则设置为最大值maxva
    //        2   THRESH_TOZERO算法，当前点值大于阈值thresh时，不改变，否则设置为0
    //        3   THRESH_TOZERO_INV算法，当前点值大于阈值thresh时，设置为0，否则不改变
    //        4   THRESH_TRUNC算法，当前点值大于阈值thresh时，设置为阈值thresh，否则不改变
    //        5   ADAPTIVE_THRESH_MEAN_C算法，自适应阈值
    //        6   ADAPTIVE_THRESH_GAUSSIAN_C算法，自适应阈值
    //        thresh阈值，maxval最大值，threshold默认保存原图。thresh和maxval同为255时灰度处理
    //* mode操作模式，后台 true，前台 false。默认前台操作
    // * @return {Promise.<boolean>}
    //*/
    function saveScreenshot(const hwnd, savePath: string; left: integer = 0; top: integer = 0; right: integer = 0; bottom: integer = 0; thresholdType: integer = 0; thresh: integer = 0; maxval: integer = 0; mode: string = 'false'): boolean;

    ///**获取指定坐标点的色值
    // * @param {string|number} hwnd  窗口句柄
    // * @param {number} x 横坐标
    // * @param {number} y 纵坐标
    // * @param {boolean} mode 操作模式，后台 true，前台 false。默认前台操作
    // * @return {Promise.<string>} 成功返回#开头的颜色值，失败返回null
    //*/
    function getColor(const hwnd, x, y: string; mode: string = 'false'): string;
    // /**找图
    // * @param {string|number} hwnd  窗口句柄
    // * @param {string} imagePath 小图片路径
    // * @param {{region:[left:number, top:number, right:number, bottom:number], sim:number, threshold:[thresholdType:number, thresh:number, maxval:number], multi:number, mode:boolean}} options 可选参数
    // * region 指定区域找图 [10, 20, 100, 200]，region默认 hwnd对应的窗口
    // * sim浮点型 图片相似度 0.0-1.0，sim默认0.95
    // * threshold二值化图片, thresholdType算法类型：
    //        0   THRESH_BINARY算法，当前点值大于阈值thresh时，取最大值maxva，否则设置为0
    //        1   THRESH_BINARY_INV算法，当前点值大于阈值thresh时，设置为0，否则设置为最大值maxva
    //        2   THRESH_TOZERO算法，当前点值大于阈值thresh时，不改变，否则设置为0
    //        3   THRESH_TOZERO_INV算法，当前点值大于阈值thresh时，设置为0，否则不改变
    //        4   THRESH_TRUNC算法，当前点值大于阈值thresh时，设置为阈值thresh，否则不改变
    //        5   ADAPTIVE_THRESH_MEAN_C算法，自适应阈值
    //        6   ADAPTIVE_THRESH_GAUSSIAN_C算法，自适应阈值
    //        thresh阈值，maxval最大值，threshold默认保存原图。thresh和maxval同为255时灰度处理
    // * multi 找图数量，默认为1 找单个图片坐标
    // * mode 操作模式，后台 true，前台 false。默认前台操作
    // * @return {Promise.<[{x:number, y:number}]>} 成功返回 单坐标点[{x:number, y:number}]，多坐标点[{x1:number, y1:number}, {x2:number, y2:number}...] 失败返回null
    //*/
    function findImage(const hwnd, imagePath: string; left: integer = 0; top: integer = 0; right: integer = 0; bottom: integer = 0; sim: string = '0.95'; thresholdType: integer = 0;
      thresh: integer = 0; maxval: integer = 0; multi: integer = 1; mode: string = 'false'): string;
    // /**找动态图
    // * @param {string|number} hwnd  窗口句柄
    // * @param {number} frameRate 前后两张图相隔的时间，单位毫秒
    // * @param {{region:[left:number, top:number, right:number, bottom:number], mode:boolean}} options 可选参数
    // * region 指定区域找图 [10, 20, 100, 200]，region默认 hwnd对应的窗口
    // * mode 操作模式，后台 true，前台 false。默认前台操作
    // * @return {Promise.<[{x:number, y:number}]>} 成功返回 单坐标点[{x:number, y:number}]，多坐标点[{x1:number, y1:number}, {x2:number, y2:number}...] 失败返回null
    //*/
    function findAnimation(const hwnd: string; frameRate: integer; left: integer = 0; top: integer = 0; right: integer = 0; bottom: integer = 0; mode: string = 'false'): string;
    ///**查找指定色值的坐标点
    // * @param {string|number} hwnd  窗口句柄
    // * @param {string} strMainColor #开头的色值
    // * @param subColors:offsetX:number/offsetY:number/strSubColor:string,...  left:number, top:number, right:number, bottom:number , sim:number, mode:boolean 可选参数
    // * subColors 相对于strMainColor 的子色值，[[offsetX, offsetY, "#FFFFFF"], ...]，subColors默认为null
    // * 指定区域找色 [10, 20, 100, 200]，region默认 hwnd对应的窗口
    // * sim相似度0.0-1.0，sim默认为1
    // * mode 操作模式，后台 true，前台 false。默认前台操作
    // * @return {Promise.<{x:number, y:number}>} 成功返回{x:number, y:number} 失败返回null
    //*/
    function findColor(const hwnd, strMainColor: string; subColors: string = ''; left: integer = 0; top: integer = 0; right: integer = 0; bottom: integer = 0; sim: string = '1'; thresholdType: integer = 0; mode: string = 'false'): string;
    ///**比较指定坐标点的颜色值
    // * @param {string|number} hwnd  窗口句柄
    // * @param {number} mainX 主颜色所在的X坐标
    // * @param {number} mainY 主颜色所在的Y坐标
    // * @param {string} strMainColor #开头的色值
    // * @param subColors:offsetX:number/offsetY:number/strSubColor:string,...  left:number, top:number, right:number, bottom:number , sim:number, mode:boolean 可选参数
    // * subColors 相对于strMainColor 的子色值，[[offsetX, offsetY, "#FFFFFF"], ...]，subColors默认为null
    // * region 指定区域找色 [10, 20, 100, 200]，region默认 hwnd对应的窗口
    // * sim相似度0.0-1.0，sim默认为1
    // * mode 操作模式，后台 true，前台 false。默认前台操作
    // * @return {Promise.<boolean>} 成功返回true 失败返回 false
    //*/
    function compareColor(const hwnd, mainX, mainY, strMainColor: string; subColors: string = ''; left: integer = 0; top: integer = 0; right: integer = 0; bottom: integer = 0; sim: string = '1';
      thresholdType: integer = 0; mode: string = 'false'): boolean;
    ///**ocr
    // * @param {string|number} hwnd  窗口句柄
    // * @param {left:number} left 左上角x点
    // * @param {top:number} top 左上角y点
    // * @param {right:number} right 右下角 x点
    // * @param {bottom:number} bottom 右下角 y点
    // * @param {mode:boolean} mode 操作模式，后台 true，前台 false。默认前台操作
    // * @return {Promise.<[]>} 失败返回null，成功返回数组形式的识别结果
    //*/
    function ocr(const hwnd: string; left, top, right, bottom: integer; mode: string = 'false'): string;
    // /**ocrByFile
    // * @param {string} imagePath 图片路径
    // * @param {left:number} left 左上角x点
    // * @param {top:number} top 左上角y点
    // * @param {right:number} right 右下角 x点
    // * @param {bottom:number} bottom 右下角 y点
    // * @return {Promise.<[]>} 失败返回null，成功返回数组形式的识别结果
    //*/
    function ocrByFile(const imagePath: string; left, top, right, bottom: integer): string;

    ///**获取屏幕文字
    // * @param {string|number} hwndOrImagePath  窗口句柄或者图片路径
    // * @param {{region:[left:number, top:number, right:number, bottom:number], mode:boolean}} options 可选参数
    // * 指定区域 [10, 20, 100, 200]，region默认全图
    // * mode 操作模式，后台 true，前台 false。默认前台操作, 仅适用于hwnd
    // * @return {Promise.<string>} 失败返回null，成功返窗口上的文字
    //*/
    function getWords(const hwndOrImagePath: string; left: integer = 0; top: integer = 0; right: integer = 0; bottom: integer = 0; mode: string = 'false'): string;

    ///**查找文字
    // * @param {string|number} hwndOrImagePath  窗口句柄或者图片路径
    // * @param {string} words 要查找的文字
    // * @param {{region:[left:number, top:number, right:number, bottom:number], mode:boolean}} options 可选参数
    // * region 指定区域 [10, 20, 100, 200]，region默认全图
    // * mode 操作模式，后台 true，前台 false。默认前台操作, 仅适用于hwnd
    // * @return {Promise.<[{x:number, y:number}]>} 失败返回null，成功返回数组[{x:number, y:number}, ...]，文字所在的坐标点
    //*/
    function findWords(const hwndOrImagePath, words: string; left: integer = 0; top: integer = 0; right: integer = 0; bottom: integer = 0; mode: string = 'false'): string;

    ///**获取指定元素名称
    // * @param {string|number} hwnd  窗口句柄
    // * @param {string} xpath 元素路径
    // * @return {Promise.<string>} 成功返回元素名称
    //*/
    function getElementName(const hwnd, xpath: string): string;
    ///**获取指定元素文本
    // * @param {string|number} hwnd  窗口句柄
    // * @param {string} xpath 元素路径
    // * @return {Promise.<string>} 成功返回元素文本
    //*/
    function getElementValue(const hwnd, xpath: string): string;
    //  /**获取指定元素矩形大小
    // * @param {string|number} hwnd  窗口句柄
    // * @param {string} xpath 元素路径
    // * @return {Promise.<{left:number, top:number, right:number, bottom:number}>} 成功返回元素位置，失败返回-1|-1|-1|-1
    //*/
    function getElementRect(const hwnd, xpath: string): string;
    ///**获取元素窗口句柄
    // * @param {string|number} hwnd  窗口句柄
    // * @param {string} xpath 元素路径
    // * @return {Promise.<string>} 成功返回元素窗口句柄，失败返回null
    //*/
    function getElementWindow(const hwnd, xpath: string): string;
    // /**点击元素
    // * @param {string|number} hwnd  窗口句柄
    // * @param {string} xpath 元素路径
    // * @param {number} msg 单击左键:1 单击右键:2 按下左键:3 弹起左键:4 按下右键:5 弹起右键:6 双击左键:7 双击右键:8
    // * @return {Promise.<boolean>} 成功返回true 失败返回 false
    //*/
    function clickElement(const hwnd, xpath: string; msg: string = '1'): boolean;
    //  /**设置指定元素作为焦点
    // * @param {string|number} hwnd  窗口句柄
    // * @param {string} xpath 元素路径
    // * @return {Promise.<boolean>} 成功返回true 失败返回 false
    //*/
    function setElementFocus(const hwnd, xpath: string): boolean;
    ///**设置元素文本
    // * @param {string|number} hwnd  窗口句柄
    // * @param {string} xpath 元素路径
    // * @param {string} value 要设置的内容
    // * @return {Promise.<boolean>} 成功返回true 失败返回 false
    //*/
    function setElementValue(const hwnd, xpath, Value: string): boolean;
    ///**滚动元素
    //  * @param {string|number} hwnd  窗口句柄
    //  * @param {string} xpath 元素路径
    //  * @param {number} horizontalPercent 水平百分比 -1不滚动
    //  * @param {number} verticalPercent 垂直百分比 -1不滚动
    //  * @return {Promise.<boolean>} 成功返回true 失败返回 false
    // */
    function setElementScroll(const hwnd, xpath: string; horizontalPercent: integer = -1; verticalPercent: integer = -1): boolean;
    ///**单/复选框是否选中
    //  * @param {string|number} hwnd  窗口句柄
    //  * @param {string} xpath 元素路径
    //  * @return {Promise.<boolean>} 成功返回true 失败返回 false
    // */
    function isSelected(const hwnd, xpath: string): boolean;
    ///**关闭窗口
    //  * @param {string|number} hwnd  窗口句柄
    //  * @param {string} xpath 元素路径
    //  * @return {Promise.<boolean>} 成功返回true 失败返回 false
    // */
    function closeWindow(const hwnd, xpath: string): boolean;
    ///**设置窗口状态
    //  * @param {string|number} hwnd  窗口句柄
    //  * @param {string} xpath 元素路径
    //  * @param {number} state 0正常 1最大化 2 最小化
    //  * @return {Promise.<boolean>} 成功返回true 失败返回 false
    // */
    function setWindowState(const hwnd, xpath: string; state: integer = 0): boolean;
    ///**设置剪切板文本
    //  * @param {string} text 设置的文本
    //  * @return {Promise.<boolean>} 成功返回true 失败返回 false
    // */
    function setClipboardText(const Text: string): boolean;
    ///**获取剪切板文本
    //  * @return {Promise.<string>} 返回剪切板文本
    // */
    function getClipboardText: string;
    ///**启动指定程序
    // * @param {string} commandLine 启动命令行
    // * @param {boolean} showWindow 是否显示窗口。可选参数,默认显示窗口
    // * @param {boolean} isWait 是否等待程序结束。可选参数,默认不等待
    // * @return {Promise.<boolean>} 成功返回true,失败返回false
    //*/
    function startProcess(const commandLine: string; showWindow: string = 'true'; isWait: string = 'false'): boolean;
    ///**执行cmd命令
    // * @param {string} command cmd命令，不能含 "cmd"字串
    // * @param {number} waitTimeout 可选参数，等待结果返回超时，单位毫秒，默认300毫秒
    // * @return {Promise.<string>} 返回cmd执行结果
    //*/
    function executeCommand(const command: string; waitTimeout: integer = 300): string;
    // /**指定url下载文件
    // * @param {string} url 文件地址
    // * @param {string} filePath 文件保存的路径
    // * @param {boolean} isWait 是否等待.为true时,等待下载完成
    // * @return {Promise.<boolean>} 总是返回true
    //*/
    procedure downloadFile(const url, filePath: string; isWait: string = 'true');
    ///**打开excel文档
    // * @param {string} excelPath excle路径
    // * @return {Promise.<Object>} 成功返回excel对象，失败返回null
    //*/
    function openExcel(const excelPath: string): string;
    ///**打开excel表格
    // * @param {Object} excelObject excel对象
    // * @param {string} sheetName 表名
    // * @return {Promise.<Object>} 成功返回sheet对象，失败返回null
    //*/
    function openExcelSheet(const excelObject, sheetName: string): string;
    // /**保存excel文档
    // * @param {Object} excelObject excel对象
    // * @return {Promise.<boolean>} 成功返回true，失败返回false
    //*/
    function saveExcel(const excelObject: string): boolean;
    // /**写入数字到excel表格
    // * @param {Object} sheetObject sheet对象
    // * @param {number} row 行
    // * @param {number} col 列
    // * @param {number} value 写入的值
    // * @return {Promise.<boolean>} 成功返回true，失败返回false
    //*/
    function writeExcelNum(const sheetObject: string; row, col, Value: integer): boolean;
    ///**写入字符串到excel表格
    // * @param {Object} sheetObject sheet对象
    // * @param {number} row 行
    // * @param {number} col 列
    // * @param {string} strValue 写入的值
    // * @return {Promise.<boolean>} 成功返回true，失败返回false
    //*/
    function writeExcelStr(const sheetObject: string; row, col: integer; strValue: string): boolean;
    ///**读取excel表格数字
    // * @param {Object} sheetObject sheet对象
    // * @param {number} row 行
    // * @param {number} col 列
    // * @return {Promise.<number>} 返回读取到的数字
    //*/
    function readExcelNum(const sheetObject: string; row, col: integer): string;
    // /**读取excel表格字串
    // * @param {Object} sheetObject sheet对象
    // * @param {number} row 行
    // * @param {number} col 列
    // * @return {Promise.<string>} 返回读取到的字符
    //*/
    function readExcelStr(const sheetObject: string; row, col: integer): string;
    ///**删除excel表格行
    // * @param {Object} sheetObject sheet对象
    // * @param {number} rowFirst 起始行
    // * @param {number} rowLast 结束行
    // * @return {Promise.<boolean>} 成功返回true，失败返回false
    //*/
    function removeExcelRow(const sheetObject: string; rowFirst, rowLast: integer): boolean;
    //  /**删除excel表格列
    // * @param {Object} sheetObject sheet对象
    // * @param {number} colFirst 起始列
    // * @param {number} colLast 结束列
    // * @return {Promise.<boolean>} 成功返回true，失败返回false
    //*/
    function removeExcelCol(const sheetObject: string; colFirst, colLast: integer): boolean;




  end;

  {$endregion}

   {$region TAndroidBotCls}

  //AndroidBot Pascal接口By 黑魔(QQ:274838061)

  { TAndroidBot }

  TAndroidBot = class(TThread)


  public
    ParentForm: TForm;
    Sock: TTCPBlockSocket;
    ClientSock: TTCPBlockSocket;

    intervalTimeout, waitTimeout: integer;

    IP:   string;
    Port: integer;


    constructor Create(port: integer = 16678);
    destructor Destroy; override;
    procedure Execute; override;


    //是否已经连接
    function IsConnected: boolean;

    function setSendData(Args: array of string): string;
    function sendData(strData: string): string;

    function sendDataWaitTrue(strData: string; waitret: string = 'false'): string;

    ///**设置隐式等待
    // * @param {number} waitMs  等待时间,单位毫秒
    // * @param {number} intervalMs 心跳间隔，单位毫秒。可选参数，默认10毫秒
    // * @return {Promise.<void>}
    //*/
    procedure setImplicitTimeout(waitMs: integer; intervalMs: integer = 10);
    ///**截图保存
    // * @param {string} savePath 保存的位置
    // * @param {{region:[left:number, top:number, right:number, bottom:number], threshold:[thresholdType:number, thresh:number, maxval:number], mode:boolean}} options 可选参数
    // * region截图区域 [10, 20, 100, 200]，region默认  hwnd对应的窗口
    // * threshold二值化图片, thresholdType算法类型：
    //        0   THRESH_BINARY算法，当前点值大于阈值thresh时，取最大值maxva，否则设置为0
    //        1   THRESH_BINARY_INV算法，当前点值大于阈值thresh时，设置为0，否则设置为最大值maxva
    //        2   THRESH_TOZERO算法，当前点值大于阈值thresh时，不改变，否则设置为0
    //        3   THRESH_TOZERO_INV算法，当前点值大于阈值thresh时，设置为0，否则不改变
    //        4   THRESH_TRUNC算法，当前点值大于阈值thresh时，设置为阈值thresh，否则不改变
    //        5   ADAPTIVE_THRESH_MEAN_C算法，自适应阈值
    //        6   ADAPTIVE_THRESH_GAUSSIAN_C算法，自适应阈值
    //        thresh阈值，maxval最大值，threshold默认保存原图。thresh和maxval同为255时灰度处理
    // * @return {Promise.<boolean>}
    //*/
    function saveScreenshot(const savePath: string; left: integer = 0; top: integer = 0; right: integer = 0; bottom: integer = 0; thresholdType: integer = 0; thresh: integer = 0; maxval: integer = 0): boolean;
    // /**获取指定坐标点的色值
    //* @param {number} x 横坐标
    //* @param {number} y 纵坐标
    //* @return {Promise.<string>} 成功返回#开头的颜色值，失败返回null
    //*/
    function getColor(x, y: integer): string;

    // /**找图
    // * @param {string} imagePath 小图片路径（手机）
    // * @param {{region:[left:number, top:number, right:number, bottom:number], sim:number, threshold:[thresholdType:number, thresh:number, maxval:number], multi:number, mode:boolean}} options 可选参数
    // * region 指定区域找图 [10, 20, 100, 200]，region默认 hwnd对应的窗口
    // * sim浮点型 图片相似度 0.0-1.0，sim默认0.95
    // * threshold二值化图片, thresholdType算法类型：
    //        0   THRESH_BINARY算法，当前点值大于阈值thresh时，取最大值maxva，否则设置为0
    //        1   THRESH_BINARY_INV算法，当前点值大于阈值thresh时，设置为0，否则设置为最大值maxva
    //        2   THRESH_TOZERO算法，当前点值大于阈值thresh时，不改变，否则设置为0
    //        3   THRESH_TOZERO_INV算法，当前点值大于阈值thresh时，设置为0，否则不改变
    //        4   THRESH_TRUNC算法，当前点值大于阈值thresh时，设置为阈值thresh，否则不改变
    //        5   ADAPTIVE_THRESH_MEAN_C算法，自适应阈值
    //        6   ADAPTIVE_THRESH_GAUSSIAN_C算法，自适应阈值
    //        thresh阈值，maxval最大值，threshold默认保存原图。thresh和maxval同为255时灰度处理
    // * multi 找图数量，默认为1 找单个图片坐标
    // * mode 操作模式，后台 true，前台 false。默认前台操作
    // * @return {Promise.<[{x:number, y:number}]>} 成功返回 单坐标点[{x:number, y:number}]，多坐标点[{x1:number, y1:number}, {x2:number, y2:number}...] 失败返回null
    //*/
    function findImage(const imagePath: string; left: integer = 0; top: integer = 0; right: integer = 0; bottom: integer = 0; sim: string = '0.95'; thresholdType: integer = 0; thresh: integer = 0;
      maxval: integer = 0; multi: integer = 1): string;
    // /**找动态图
    // * @param {number} frameRate 前后两张图相隔的时间，单位毫秒
    // * @param {{region:[left:number, top:number, right:number, bottom:number], mode:boolean}} options 可选参数
    // * region 指定区域找图 [10, 20, 100, 200]，region默认 hwnd对应的窗口
    // * @return {Promise.<[{x:number, y:number}]>} 成功返回 单坐标点[{x:number, y:number}]，多坐标点[{x1:number, y1:number}, {x2:number, y2:number}...] 失败返回null
    //*/
    function findAnimation(frameRate: integer; left: integer = 0; top: integer = 0; right: integer = 0; bottom: integer = 0): string;
    ///**查找指定色值的坐标点
    // * @param {string} strMainColor #开头的色值
    // * @param subColors:offsetX:number/offsetY:number/strSubColor:string,...  left:number, top:number, right:number, bottom:number , sim:number, mode:boolean 可选参数
    // * subColors 相对于strMainColor 的子色值，[[offsetX, offsetY, "#FFFFFF"], ...]，subColors默认为null
    // * 指定区域找色 [10, 20, 100, 200]，region默认 hwnd对应的窗口
    // * sim相似度0.0-1.0，sim默认为1
    // * @return {Promise.<{x:number, y:number}>} 成功返回{x:number, y:number} 失败返回null
    //*/
    function findColor(const strMainColor: string; subColors: string = ''; left: integer = 0; top: integer = 0; right: integer = 0; bottom: integer = 0; sim: string = '1'; thresholdType: integer = 0): string;
    ///**比较指定坐标点的颜色值
    // * @param {number} mainX 主颜色所在的X坐标
    // * @param {number} mainY 主颜色所在的Y坐标
    // * @param {string} strMainColor #开头的色值
    // * @param subColors:offsetX:number/offsetY:number/strSubColor:string,...  left:number, top:number, right:number, bottom:number , sim:number, mode:boolean 可选参数
    // * subColors 相对于strMainColor 的子色值，[[offsetX, offsetY, "#FFFFFF"], ...]，subColors默认为null
    // * region 指定区域找色 [10, 20, 100, 200]，region默认 hwnd对应的窗口
    // * sim相似度0.0-1.0，sim默认为1
    // * @return {Promise.<boolean>} 成功返回true 失败返回 false
    //*/
    function compareColor(const mainX, mainY, strMainColor: string; subColors: string = ''; left: integer = 0; top: integer = 0; right: integer = 0; bottom: integer = 0; sim: string = '1'; thresholdType: integer = 0): boolean;
    ///**手指按下
    //* @param {number} x 横坐标
    //* @param {number} y 纵坐标
    //* @param {number} duration 按下时长，单位毫秒
    //* @return {Promise.<boolean>} 成功返回true 失败返回false
    //*/
    function press(x, y, duration: integer): boolean;
    ///**手指移动
    //* @param {number} x 横坐标
    //* @param {number} y 纵坐标
    //* @param {number} duration 移动时长，单位毫秒
    //* @return {Promise.<boolean>} 成功返回true 失败返回false
    //*/
    function move(x, y, duration: integer): boolean;
    ///**手指释放
    //* @return {Promise.<boolean>} 成功返回true 失败返回false
    //*/
    function Release: boolean;
    ///**点击坐标
    //* @param {number} x 横坐标
    //* @param {number} y 纵坐标
    //* @return {Promise.<boolean>} 成功返回true 失败返回false
    //*/
    function click(x, y: integer): boolean;
    ///**双击坐标
    //* @param {number} x 横坐标
    //* @param {number} y 纵坐标
    //* @return {Promise.<boolean>} 成功返回true 失败返回false
    //*/
    function doubleClick(x, y: integer): boolean;
    ///**长按坐标
    //* @param {number} x 横坐标
    //* @param {number} y 纵坐标
    //* @param {number} duration 长按时长，单位毫秒
    //* @return {Promise.<boolean>} 成功返回true 失败返回false
    //*/
    function longClick(x, y, duration: integer): boolean;
    ///**滑动坐标
    //* @param {number} startX 起始横坐标
    //* @param {number} startY 起始纵坐标
    //* @param {number} endX 结束横坐标
    //* @param {number} endY 结束纵坐标
    //* @param {number} duration 滑动时长，单位毫秒
    //* @return {Promise.<boolean>} 成功返回true 失败返回false
    //*/
    function swipe(startX, startY, endX, endY, duration: integer): boolean;
    ///**执行手势
    //* @param  gesturePath 手势路径 示例：x/y,x1/y1,x2/y2...
    //* @param {number} duration 手势时长，单位毫秒
    //* @return {Promise.<boolean>} 成功返回true 失败返回false
    //*/
    function dispatchGesture(const gesturePath: string; duration: integer): boolean;
    ///**执行多个手势
    //* @param gesturesPath  多点手势路径   示例：x/y,x1/y1,x2/y2...|x/y,x1,y1...
    //* @return {Promise.<boolean>} 成功返回true 失败返回false
    //*/
    function dispatchGestures(const gesturesPath: string): boolean;
    ///**发送文本
    //* @param {string} text 发送的文本，需要打开aibote输入法
    //* @return {Promise.<boolean>} 成功返回true 失败返回false
    //*/
    function sendKeys(const Text: string): boolean;
    ///**发送按键
    // * @param {number} keyCode 发送的虚拟按键，需要打开aibote输入法。例如：最近应用列表：187  回车：66
    // * 按键对照表 https://blog.csdn.net/yaoyaozaiye/article/details/122826340
    // * @return {Promise.<boolean>} 成功返回true 失败返回false
    //*/
    function sendVk(keyCode: integer): boolean;
    ///**返回
    //* @return {Promise.<boolean>} 成功返回true 失败返回false
    //*/
    function back: boolean;
    ///**home
    //* @return {Promise.<boolean>} 成功返回true 失败返回false
    //*/
    function home: boolean;
    ///**显示最近任务
    //* @return {Promise.<boolean>} 成功返回true 失败返回false
    //*/
    function recents: boolean;
    ///**Toast消息提示
    //* @param {string} text 提示的文本
    //* @return {Promise.<boolean>} 返回true
    //*/
    function showToast(const Text: string): boolean;
    ///**启动App
    //* @param {string} name 包名或者app名称
    //* @return {Promise.<boolean>} 成功返回true 失败返回false
    //*/
    function startApp(const Name: string): boolean;
    ///**屏幕大小
    //* @return {width|height} 成功返回{width|height} 600|1200
    //*/
    function getWindowSize: string;
    ///**图片大小
    //* @param {string} imagePath 图片路径
    //* @return {width|height} 成功返回{width|height} 600|1200
    //*/
    function getImageSize(const imagePath: string): string;
    ///**获取安卓ID
    //* @return {Promise.<string>} 成功返回安卓手机ID
    //*/
    function getAndroidId: string;
    ///**ocr
    // * @param {left:number} left 左上角x点
    // * @param {top:number} top 左上角y点
    // * @param {right:number} right 右下角 x点
    // * @param {bottom:number} bottom 右下角 y点
    // * @param {scale:number} scale 图片缩放率, 默认为 1.0 原大小。大于1.0放大，小于1.0缩小，不能为负数。
    // * @return {Promise.<[]>} 失败返回null，成功返回数组形式的识别结果
    //*/
    function ocr(left, top, right, bottom: integer; scale: string = '1'): string;

    ///**获取元素位置
    //* @param {string} xpath 元素路径
    //* @return {Promise.<{left:number, top:number, right:number, bottom:number}>} 成功返回元素位置，失败返回-1|-1|-1|-1
    //*/
    function getElementRect(const xpath: string): string;
    ///**获取元素描述
    //* @param {string} xpath 元素路径
    //* @return {Promise.<string>} 成功返回元素内容，失败返回null
    //*/
    function getElementDescription(const xpath: string): string;
    ///**获取元素文本
    //* @param {string} xpath 元素路径
    //* @return {Promise.<string>} 成功返回元素内容，失败返回null
    //*/
    function getElementText(const xpath: string): string;
    ///**判断元素是否可见
    // * @param {string} xpath 元素路径
    // * @return {Promise.<boolean>} 可见 ture，不可见 false
    //*/
    function elementIsVisible(const xpath: string): boolean;

    ///**设置元素文本
    //* @param {string} xpath 元素路径
    //* @param {string} text 设置的文本
    //* @return {Promise.<boolean>} 成功返回true 失败返回false
    //*/
    function setElementText(const xpath, Text: string): boolean;
    ///**点击元素
    //* @param {string} xpath 元素路径
    //* @return {Promise.<boolean>} 成功返回true 失败返回false
    //*/
    function clickElement(const xpath: string): boolean;
    ///**滚动元素
    //* @param {string} xpath 元素路径
    //* @param {number} direction 0 向前滑动， 1 向后滑动
    //* @return {Promise.<boolean>} 成功返回true 失败返回false
    //*/
    function scrollElement(const xpath: string; direction: integer = 0): boolean;
    ///**判断元素是否存在
    //* @param {string} xpath 元素路径
    //* @return {Promise.<boolean>} 成功返回true 失败返回false
    //*/
    function existsElement(const xpath: string): boolean;
    ///**判断元素是否选中
    //* @param {string} xpath 元素路径
    //* @return {Promise.<boolean>} 成功返回true 失败返回false
    //*/
    function isSelectedElement(const xpath: string): boolean;
    ///**上传文件
    //* @param {string} windowsFilePath 电脑文件路径，注意电脑路径 "\\"转义问题
    //* @param {string} androidFilePath 安卓文件保存路径, 安卓外部存储根目录 /storage/emulated/0/
    //* @return {Promise.<boolean>} 成功返回true 失败返回false
    //*/
    function pushFile(const windowsFilePath, androidFilePath: string): boolean;
    ///**拉取文件
    //* @param {string} androidFilePath 安卓文件路径，安卓外部存储根目录 /storage/emulated/0/
    //* @param {string} windowsFilePath 电脑文件保存路径，注意电脑路径 "\\"转义问题
    //* @return {Promise.<void>}
    //*/
    procedure pullFile(const androidFilePath, windowsFilePath: string);
    ///**写入安卓文件
    //* @param {string} androidFilePath 安卓文件路径，安卓外部存储根目录 /storage/emulated/0/
    //* @param {string} text 写入的内容
    //* @param {boolean} isAppend 可选参数，是否追加，默认覆盖文件内容
    //* @return {Promise.<boolean>} 成功返回true，失败返回 false
    //*/
    function writeAndroidFile(const androidFilePath, Text: string; isAppend: string = 'false'): boolean;
    ///**读取安卓文件
    //* @param {string} androidFilePath 安卓文件路径，安卓外部存储根目录 /storage/emulated/0/
    //* @return {Promise.<string>} 成功返回文件内容，失败返回 null
    //*/
    function readAndroidFile(const androidFilePath: string): string;
    ///**跳转uri
    //* @param {string} uri 跳转链接，例如：打开支付宝扫一扫界面，"alipayqr://platformapi/startapp?saId=10000007"
    //* @return {Promise.<boolean>} 成功返回true，失败返回 false
    //*/
    function openUri(const uri: string): boolean;
    ///**拨打电话
    //* @param {string} phoneNumber 拨打的电话号码
    //* @return {Promise.<boolean>} 成功返回true，失败返回 false
    //*/
    function callPhone(const phoneNumber: string): boolean;
    ///**发送短信
    //* @param {string} phoneNumber 发送的电话号码
    //* @param {string} message 短信内容
    //* @return {Promise.<boolean>} 成功返回true，失败返回 false
    //*/
    function sendMsg(const phoneNumber, message: string): boolean;

    ///**获取当前活动窗口(Activity)
    // * @return {Promise.<string>} 成功返回当前activity
    //*/
    function getActivity: string;
    ///**获取当前活动包名(Package)
    // * @return {Promise.<string>} 成功返回当前包名
    //*/
    function getPackage: string;

    ///**设置剪切板文本
    //* @param {string} text 设置的文本
    //* @return {Promise.<boolean>} 成功返回true，失败返回 false
    //*/
    function setClipboardText(const Text: string): boolean;
    ///**获取剪切板文本
    // * @return {Promise.<string>} 需要打开aibote输入法。成功返回剪切板文本，失败返回null
    //*/
    function getClipboardText: string;
    ///**创建TextView控件
    // * @param {number} id 控件ID，不可与其他控件重复
    // * @param {string} text 控件文本
    // * @param {number} x 控件在屏幕上x坐标
    // * @param {number} y 控件在屏幕上y坐标
    // * @param {number} width 控件宽度
    // * @param {number} height 控件高度
    //* @return {Promise.<boolean>} 成功返回true，失败返回 false
    //*/
    function createTextView(const id, Text: string; x, y, Width, Height: integer): boolean;
    ///**创建EditText控件
    // * @param {number} id 控件ID，不可与其他控件重复
    // * @param {string} hintText 提示文本
    // * @param {number} x 控件在屏幕上x坐标
    // * @param {number} y 控件在屏幕上y坐标
    // * @param {number} width 控件宽度
    // * @param {number} height 控件高度
    //* @return {Promise.<boolean>} 成功返回true，失败返回 false
    //*/
    function createEditText(const id, hintText: string; x, y, Width, Height: integer): boolean;
    ///**创建CheckBox控件
    // * @param {number} id 控件ID，不可与其他控件重复
    // * @param {string} text 控件文本
    // * @param {number} x 控件在屏幕上x坐标
    // * @param {number} y 控件在屏幕上y坐标
    // * @param {number} width 控件宽度
    // * @param {number} height 控件高度
    //* @return {Promise.<boolean>} 成功返回true，失败返回 false
    //*/
    function createCheckBox(const id, Text: string; x, y, Width, Height: integer): boolean;
    ///**创建WebView控件
    // * @param {number} id 控件ID，不可与其他控件重复
    // * @param {string} url 加载的链接
    // * @param {number} x 控件在屏幕上x坐标，值为-1时自动填充宽高
    // * @param {number} y 控件在屏幕上y坐标，值为-1时自动填充宽高
    // * @param {number} width 控件宽度，值为-1时自动填充宽高
    // * @param {number} height 控件高度，值为-1时自动填充宽高
    //* @return {Promise.<boolean>} 成功返回true，失败返回 false
    //*/
    function createWebView(const id, url: string; x, y, Width, Height: integer): boolean;
    // /**清除脚本控件
    //* @return {Promise.<boolean>} 成功返回true，失败返回 false
    //*/
    function clearScriptControl: boolean;

    ///**获取脚本配置参数
    //* @return {Promise.<JSON>} 成功返回{"id":"text", "id":"isSelect"} 此类对象，失败返回null。函数仅返回TextEdit和CheckBox控件值，需要用户点击安卓端 "提交参数" 按钮
    //*/
    function getScriptParam: string;



  end;

  {$endregion}



implementation

{$region TAndroidBot }

constructor TAndroidBot.Create(port: integer);
begin
  inherited Create(False);
  waitTimeout := 5000;
  intervalTimeout := 200;
  sock      := TTCPBlockSocket.Create;
  sock.RaiseExcept := False;
  FreeOnTerminate := True;
  self.Port := port;
end;

destructor TAndroidBot.Destroy;
begin
  inherited Destroy;
  try

    if ClientSock <> nil then
    begin
      ClientSock.Free;
    end;
    sock.Free;

  except
  end;
end;

procedure TAndroidBot.Execute;
var
  CSock: TSocket; //One per client connection, use array or list if multiple connection is possible
begin

  with sock do
  begin
    CreateSocket;
    setLinger(True, 10000);
    bind('0.0.0.0', port.ToString);
    listen;

    repeat
      if terminated then break;
      if canread(1000) then
      begin
        CSock := accept;
        if lastError = 0 then
        begin
          ClientSock := TTCPBlockSocket.Create;
          ClientSock.socket := CSock;
          ClientSock.GetSins;

        end;
      end;
    until False;

    AbortSocket; // Free socket
  end;

end;

function TAndroidBot.IsConnected: boolean;
begin
  Result := (clientsock <> nil) and (clientsock.CanWrite(100));
end;

function TAndroidBot.setSendData(Args: array of string): string;
var
  i: integer;
  tempStr, strData, tstr: string;
begin
  Result  := '';
  strData := '';
  tempStr := '';
  for i := 0 to High(Args) do
  begin
    tstr    := Args[i];
    strData := strData + tencoding.UTF8.GetByteCount(UTF8Decode(tstr)).ToString + '/';
    tempStr := tempStr + tstr;

  end;
  strData := strdata + #10 + tempStr;
  Result  := strdata;

end;

function TAndroidBot.sendData(strData: string): string;
var
  tmpstr: string;
  //strlen: integer;
begin
  Result := '';
  if clientsock = nil then
  begin
    Result := '';
    exit;
  end;

  clientsock.SendString(UTF8Encode(strdata));
  tmpstr := clientsock.RecvPacket(5000);
  while (clientsock.CanRead(200)) do
  begin
    tmpstr := tmpstr + clientsock.RecvPacket(200);
    if clientsock.LastError = WSAETIMEDOUT then
      break;
  end;
  if tmpstr.Contains('/') then
  begin
    //strlen := StrToInt(tmpstr.Substring(0, tmpstr.IndexOf('/')));
    Result := tmpstr.Substring(tmpstr.IndexOf('/') + 1);
  end
  else
  begin
    Result := tmpstr;
  end;

end;

function TAndroidBot.sendDataWaitTrue(strData: string; waitret: string): string;
var
  dstart: tdatetime;
begin
  dstart := now;
  Result := sendData(strdata);
  while (Result = waitret) do
  begin
    sleep(intervalTimeout);
    Result := sendData(strdata);
    if (MilliSecondsBetween(now, dstart) > waitTimeout) then
    begin
      break;
    end;
  end;

end;

procedure TAndroidBot.setImplicitTimeout(waitMs: integer; intervalMs: integer);
begin
  waitTimeout     := waitMs;
  intervalTimeout := intervalMs;
end;

function TAndroidBot.saveScreenshot(const savePath: string; left: integer; top: integer; right: integer; bottom: integer; thresholdType: integer; thresh: integer; maxval: integer): boolean;
var
  strData, strRet: string;
begin
  if (thresholdType = 5) or (thresholdType = 6) then
  begin
    thresh := 127;
    maxval := 255;
  end;

  strData := setSendData(['saveScreenshot', savePath, left.ToString, top.ToString, right.ToString, bottom.ToString, thresholdType.ToString, thresh.ToString, maxval.ToString]);
  Result  := sendData(strdata) = 'true';

end;

function TAndroidBot.getColor(x, y: integer): string;
var
  strData: string;
begin

  strData := setSendData(['getColor', x.ToString, y.ToString]);
  Result  := sendData(strdata);

end;

function TAndroidBot.findImage(const imagePath: string; left: integer; top: integer; right: integer; bottom: integer; sim: string; thresholdType: integer; thresh: integer; maxval: integer; multi: integer): string;
var
  strData: string;
begin

  strData := setSendData(['findImage', imagePath, left.ToString, top.ToString, right.ToString, bottom.ToString, sim, thresholdType.ToString, thresh.ToString, maxval.ToString, multi.ToString]);
  Result  := sendDataWaitTrue(strdata, '-1|-1');

end;

function TAndroidBot.findAnimation(frameRate: integer; left: integer; top: integer; right: integer; bottom: integer): string;
var
  strData: string;
begin

  strData := setSendData(['findAnimation', frameRate.ToString, left.ToString, top.ToString, right.ToString, bottom.ToString]);
  Result  := sendDataWaitTrue(strdata, '-1|-1');

end;

function TAndroidBot.findColor(const strMainColor: string; subColors: string; left: integer; top: integer; right: integer; bottom: integer; sim: string; thresholdType: integer): string;
var
  strData: string;
begin

  strData := setSendData(['findColor', strMainColor, subColors.Replace(',', #10), left.ToString, top.ToString, right.ToString, bottom.ToString, sim]);
  Result  := sendDataWaitTrue(strdata, '-1|-1');

end;

function TAndroidBot.compareColor(const mainX, mainY, strMainColor: string; subColors: string; left: integer; top: integer; right: integer; bottom: integer; sim: string; thresholdType: integer): boolean;
var
  strData: string;
begin

  strData := setSendData(['compareColor', mainX, mainY, strMainColor, subColors.Replace(',', #10), left.ToString, top.ToString, right.ToString, bottom.ToString, sim]);
  Result  := sendDataWaitTrue(strdata) = 'true';

end;

function TAndroidBot.press(x, y, duration: integer): boolean;
var
  strData: string;
begin

  strData := setSendData(['press', x.ToString, y.ToString, duration.ToString]);
  Result  := sendData(strdata) = 'true';

end;

function TAndroidBot.move(x, y, duration: integer): boolean;
var
  strData: string;
begin

  strData := setSendData(['move', x.ToString, y.ToString, duration.ToString]);
  Result  := sendData(strdata) = 'true';

end;

function TAndroidBot.Release: boolean;
var
  strData: string;
begin

  strData := setSendData(['release']);
  Result  := sendData(strdata) = 'true';

end;

function TAndroidBot.click(x, y: integer): boolean;
var
  strData: string;
begin

  strData := setSendData(['click', x.ToString, y.ToString]);
  Result  := sendData(strdata) = 'true';

end;

function TAndroidBot.doubleClick(x, y: integer): boolean;
var
  strData: string;
begin

  strData := setSendData(['doubleClick', x.ToString, y.ToString]);
  Result  := sendData(strdata) = 'true';

end;

function TAndroidBot.longClick(x, y, duration: integer): boolean;
var
  strData: string;
begin

  strData := setSendData(['longClick', x.ToString, y.ToString, duration.ToString]);
  Result  := sendData(strdata) = 'true';

end;

function TAndroidBot.swipe(startX, startY, endX, endY, duration: integer): boolean;
var
  strData: string;
begin

  strData := setSendData(['swipe', startX.ToString, startY.ToString, endX.ToString, endY.ToString, duration.ToString]);
  Result  := sendData(strdata) = 'true';

end;

function TAndroidBot.dispatchGesture(const gesturePath: string; duration: integer): boolean;
var
  strData: string;
begin

  strData := setSendData(['dispatchGesture', gesturePath.Replace(',', #10, [rfReplaceAll]), duration.ToString]);
  Result  := sendData(strdata) = 'true';

end;

function TAndroidBot.dispatchGestures(const gesturesPath: string): boolean;
var
  strData: string;
begin

  strData := setSendData(['dispatchGestures', gesturesPath.Replace(',', #10, [rfReplaceAll]).Replace('|', #13#10, [rfReplaceAll])]);
  Result  := sendData(strdata) = 'true';

end;

function TAndroidBot.sendKeys(const Text: string): boolean;
var
  strData: string;
begin

  strData := setSendData(['sendKeys', Text]);
  Result  := sendData(strdata) = 'true';

end;

function TAndroidBot.sendVk(keyCode: integer): boolean;
var
  strData: string;
begin

  strData := setSendData(['sendVk', keyCode.ToString]);
  Result  := sendData(strdata) = 'true';

end;

function TAndroidBot.back: boolean;
var
  strData: string;
begin

  strData := setSendData(['back']);
  Result  := sendData(strdata) = 'true';

end;

function TAndroidBot.home: boolean;
var
  strData: string;
begin

  strData := setSendData(['home']);
  Result  := sendData(strdata) = 'true';

end;

function TAndroidBot.recents: boolean;
var
  strData: string;
begin

  strData := setSendData(['recents']);
  Result  := sendData(strdata) = 'true';

end;

function TAndroidBot.showToast(const Text: string): boolean;
var
  strData: string;
begin

  strData := setSendData(['showToast', Text]);
  Result  := sendData(strdata) = 'true';

end;

function TAndroidBot.startApp(const Name: string): boolean;
var
  strData: string;
begin

  strData := setSendData(['startApp', Name]);
  Result  := sendData(strdata) = 'true';

end;

function TAndroidBot.getWindowSize: string;
var
  strData: string;
begin

  strData := setSendData(['getWindowSize']);
  Result  := sendData(strdata);

end;

function TAndroidBot.getImageSize(const imagePath: string): string;
var
  strData: string;
begin

  strData := setSendData(['getImageSize', imagePath]);
  Result  := sendData(strdata);

end;

function TAndroidBot.getAndroidId: string;
var
  strData: string;
begin

  strData := setSendData(['getAndroidId']);
  Result  := sendData(strdata);

end;

function TAndroidBot.ocr(left, top, right, bottom: integer; scale: string): string;
var
  strData: string;
begin

  strData := setSendData(['ocr', left.ToString, top.ToString, right.ToString, bottom.ToString, scale]);
  Result  := sendData(strdata);

end;

function TAndroidBot.getElementRect(const xpath: string): string;
var
  strData: string;
begin

  strData := setSendData(['getElementRect', xpath]);
  Result  := sendDataWaitTrue(strdata, '-1|-1|-1|-1');

end;

function TAndroidBot.getElementDescription(const xpath: string): string;
var
  strData: string;
begin

  strData := setSendData(['getElementDescription', xpath]);
  Result  := sendDataWaitTrue(strdata, 'null');

end;

function TAndroidBot.getElementText(const xpath: string): string;
var
  strData: string;
begin

  strData := setSendData(['getElementText', xpath]);
  Result  := sendDataWaitTrue(strdata, 'null');

end;

function TAndroidBot.elementIsVisible(const xpath: string): boolean;
var
  windowRect, elementRect: string;
  left, right, top, bottom, elementWidth, elementHeight, wwidth, wheight: integer;
  tmpArray: tarray<string>;
begin
  windowRect  := getWindowSize;
  elementRect := getElementRect(xpath);
  if (elementRect = 'null') then
  begin
    Result := False;
    exit;
  end;
  tmpArray := windowRect.Split('|');
  wwidth   := StrToInt(tmpArray[0]);
  wheight  := StrToInt(tmpArray[1]);
  tmpArray := elementRect.Split('|');
  left     := StrToInt(tmpArray[0]);
  top      := StrToInt(tmpArray[1]);
  right    := StrToInt(tmpArray[2]);
  bottom   := StrToInt(tmpArray[3]);



  elementWidth  := right - left;
  elementHeight := bottom - top;
  if (top < 0) or (left < 0) or (elementWidth > wwidth) or (elementHeight > wheight) then
    Result := False
  else
    Result := True;
end;

function TAndroidBot.setElementText(const xpath, Text: string): boolean;
var
  strData: string;
begin

  strData := setSendData(['setElementText', xpath,text]);
  Result  := sendDataWaitTrue(strdata)= 'true';

end;

function TAndroidBot.clickElement(const xpath: string): boolean;
var
  strData: string;
begin

  strData := setSendData(['clickElement', xpath]);
  Result  := sendDataWaitTrue(strdata)= 'true';

end;

function TAndroidBot.scrollElement(const xpath: string; direction: integer): boolean;
var
  strData: string;
begin

  strData := setSendData(['scrollElement', xpath,direction.ToString]);
  Result  := sendDataWaitTrue(strdata) = 'true';

end;

function TAndroidBot.existsElement(const xpath: string): boolean;
var
  strData: string;
begin

  strData := setSendData(['existsElement', xpath]);
  Result  := sendData(strdata) = 'true';

end;

function TAndroidBot.isSelectedElement(const xpath: string): boolean;
var
  strData: string;
begin

  strData := setSendData(['isSelectedElement', xpath]);
  Result  := sendData(strdata) = 'true';

end;

function TAndroidBot.pushFile(const windowsFilePath, androidFilePath: string): boolean;
var
  strData: string;
  datas:tstringstream;
begin
  //datas := tstringstream.Create('',tencoding.UTF8);
  //datas.LoadFromFile(windowsFilePath);
  //
  //strData := setSendData(['pushFile', xpath]);
  //Result  := sendData(strdata) = 'true';
  result := false;

end;

procedure TAndroidBot.pullFile(const androidFilePath, windowsFilePath: string);
begin

end;

function TAndroidBot.writeAndroidFile(const androidFilePath, Text: string; isAppend: string): boolean;
var
  strData: string;
begin

  strData := setSendData(['writeAndroidFile', androidFilePath, Text,isAppend]);
  Result  := sendData(strdata) = 'true';

end;

function TAndroidBot.readAndroidFile(const androidFilePath: string): string;
var
  strData: string;
begin

  strData := setSendData(['readAndroidFile', androidFilePath]);
  Result  := sendData(strdata);

end;

function TAndroidBot.openUri(const uri: string): boolean;
var
  strData: string;
begin

  strData := setSendData(['openUri', uri]);
  Result  := sendData(strdata) = 'true';

end;

function TAndroidBot.callPhone(const phoneNumber: string): boolean;
var
  strData: string;
begin

  strData := setSendData(['callPhone', phoneNumber]);
  Result  := sendData(strdata) = 'true';

end;

function TAndroidBot.sendMsg(const phoneNumber, message: string): boolean;
var
  strData: string;
begin

  strData := setSendData(['sendMsg', phoneNumber,message]);
  Result  := sendData(strdata) = 'true';

end;

function TAndroidBot.getActivity: string;
var
  strData: string;
begin

  strData := setSendData(['getActivity']);
  Result  := sendData(strdata);

end;

function TAndroidBot.getPackage: string;
var
  strData: string;
begin

  strData := setSendData(['getPackage']);
  Result  := sendData(strdata);

end;

function TAndroidBot.setClipboardText(const Text: string): boolean;
var
  strData: string;
begin

  strData := setSendData(['setClipboardText', Text]);
  Result  := sendData(strdata) = 'true';

end;

function TAndroidBot.getClipboardText: string;
var
  strData: string;
begin

  strData := setSendData(['getClipboardText']);
  Result  := sendData(strdata);

end;

function TAndroidBot.createTextView(const id, Text: string; x, y, Width, Height: integer): boolean;
var
  strData: string;
begin

  strData := setSendData(['createTextView', id, Text,x.ToString, y.ToString, width.ToString, height.ToString]);
  Result  := sendData(strdata) = 'true';

end;

function TAndroidBot.createEditText(const id, hintText: string; x, y, Width, Height: integer): boolean;
var
  strData: string;
begin

  strData := setSendData(['createEditText', id, hintText,x.ToString, y.ToString, width.ToString, height.ToString]);
  Result  := sendData(strdata) = 'true';

end;

function TAndroidBot.createCheckBox(const id, Text: string; x, y, Width, Height: integer): boolean;
var
  strData: string;
begin

  strData := setSendData(['createCheckBox', id, Text,x.ToString, y.ToString, width.ToString, height.ToString]);
  Result  := sendData(strdata) = 'true';

end;

function TAndroidBot.createWebView(const id, url: string; x, y, Width, Height: integer): boolean;
var
  strData: string;
begin

  strData := setSendData(['createWebView', id, url,x.ToString, y.ToString, width.ToString, height.ToString]);
  Result  := sendData(strdata) = 'true';

end;

function TAndroidBot.clearScriptControl: boolean;
var
  strData: string;
begin

  strData := setSendData(['clearScriptControl']);
  Result  := sendData(strdata) = 'true';

end;

function TAndroidBot.getScriptParam: string;
var
  strData: string;
begin

  strData := setSendData(['getScriptParam']);
  Result  := sendData(strdata);

end;

{$endregion}

{$region TWindowsBot }

constructor TWindowsBot.Create(pform: TForm; ip: string; port: integer; driverpath: string);
begin
  inherited Create(False);
  ParentForm := pform;

  waitTimeout := 5000;
  intervalTimeout := 200;
  sock := TTCPBlockSocket.Create;
  sock.RaiseExcept := False;
  FreeOnTerminate := True;
  self.Driverpath := Driverpath;
  if (self.Driverpath = '') then
  begin
    self.Driverpath := 'WindowsDriver.exe';
  end;
  self.IP   := ip;
  self.Port := port;
end;

destructor TWindowsBot.Destroy;
begin
  inherited Destroy;
  try

    if ClientSock <> nil then
    begin
      ClientSock.Free;
    end;
    sock.Free;
    if proc <> nil then
    begin
      Proc.Free;
    end;
  except
  end;
end;

procedure TWindowsBot.Execute;
var
  CSock: TSocket; //One per client connection, use array or list if multiple connection is possible
begin
  RunDriver;
  with sock do
  begin
    CreateSocket;
    setLinger(True, 10000);
    bind(IP, port.ToString);
    listen;

    repeat
      if terminated then break;
      if canread(1000) then
      begin
        CSock := accept;
        if lastError = 0 then
        begin
          ClientSock := TTCPBlockSocket.Create;
          ClientSock.socket := CSock;
          ClientSock.GetSins;

        end;
      end;
    until False;

    AbortSocket; // Free socket
  end;

end;

procedure TWindowsBot.RunDriver;
begin

  try

    Proc := tprocess.Create(ParentForm);
    proc.InheritHandles := True;

    Proc.Parameters.Clear;
    Proc.Executable := driverpath;
    Proc.Parameters.add(ip);
    Proc.Parameters.add(Port.ToString);

    Proc.Execute;

  except
  end;

end;

function TWindowsBot.IsConnected: boolean;
begin
  Result := (clientsock <> nil) and (clientsock.CanWrite(100));
end;

function TWindowsBot.setSendData(Args: array of string): string;
var
  i: integer;
  tempStr, strData, tstr: string;
begin
  Result  := '';
  strData := '';
  tempStr := '';
  for i := 0 to High(Args) do
  begin
    tstr    := Args[i];
    strData := strData + tencoding.UTF8.GetByteCount(UTF8Decode(tstr)).ToString + '/';
    tempStr := tempStr + tstr;

  end;
  strData := strdata + #10 + tempStr;
  Result  := strdata;

end;

function TWindowsBot.sendData(strData: string): string;
var
  tmpstr: string;
  //strlen: integer;
begin
  Result := '';
  if clientsock = nil then
  begin
    Result := '';
    exit;
  end;

  clientsock.SendString(UTF8Encode(strdata));
  tmpstr := clientsock.RecvPacket(5000);
  while (clientsock.CanRead(200)) do
  begin
    tmpstr := tmpstr + clientsock.RecvPacket(200);
    if clientsock.LastError = WSAETIMEDOUT then
      break;
  end;
  if tmpstr.Contains('/') then
  begin
    //strlen := StrToInt(tmpstr.Substring(0, tmpstr.IndexOf('/')));
    Result := tmpstr.Substring(tmpstr.IndexOf('/') + 1);
  end
  else
  begin
    Result := tmpstr;
  end;

end;

function TWindowsBot.sendDataWaitTrue(strData: string; waitret: string): string;
var
  dstart: tdatetime;
begin
  dstart := now;
  Result := sendData(strdata);
  while (Result = waitret) do
  begin
    sleep(intervalTimeout);
    Result := sendData(strdata);
    if (MilliSecondsBetween(now, dstart) > waitTimeout) then
    begin
      break;
    end;
  end;

end;

procedure TWindowsBot.setImplicitTimeout(waitMs: integer; intervalMs: integer);
begin
  waitTimeout     := waitMs;
  intervalTimeout := intervalMs;
end;

function TWindowsBot.findWindow(const ClassName, windowName: string): string;
var
  strData: string;
begin

  strData := setSendData(['findWindow', ClassName, windowName]);
  Result  := sendData(strdata);
end;

function TWindowsBot.findWindows(const ClassName, windowName: string): tarray<string>;
var
  strData, strret: string;
begin

  strData := setSendData(['findWindows', ClassName, windowName]);
  strret  := sendData(strdata);
  if strret.Equals('null') then
  begin
    Result := tarray<string>.Create;
  end
  else
  begin
    Result := strret.Split('|');
  end;

end;

function TWindowsBot.findSubWindow(const curHwnd, ClassName, windowName: string): string;
var
  strData: string;
begin

  strData := setSendData(['findSubWindow', curHwnd, ClassName, windowName]);
  Result  := sendData(strdata);

end;

function TWindowsBot.findParentWindow(const curHwnd: string): string;
var
  strData: string;
begin

  strData := setSendData(['findParentWindow', curHwnd]);
  Result  := sendData(strdata);

end;

function TWindowsBot.findDesktopWindow: string;
var
  strData: string;
begin

  strData := setSendData(['findDesktopWindow']);
  Result  := sendData(strdata);

end;

function TWindowsBot.getWindowName(const hwnd: string): string;
var
  strData: string;
begin

  strData := setSendData(['getWindowName', hwnd]);
  Result  := sendData(strdata);

end;

function TWindowsBot.showWindow(const hwnd, isShow: string): boolean;
var
  strData, strRet: string;
begin
  Result  := False;
  strData := setSendData(['showWindow', hwnd, isShow]);
  strret  := sendData(strdata);
  if strret = 'true' then
  begin
    Result := True;
  end;

end;

function TWindowsBot.setWindowTop(const hwnd, isTop: string): boolean;
var
  strData, strRet: string;
begin
  Result  := False;
  strData := setSendData(['setWindowTop', hwnd, isTop]);
  strret  := sendData(strdata);
  if strret = 'true' then
  begin
    Result := True;
  end;

end;

function TWindowsBot.getWindowPos(const hwnd: string): string;
var
  strData: string;
begin

  strData := setSendData(['getWindowPos', hwnd]);
  Result  := sendData(strdata);

end;

function TWindowsBot.setWindowPos(const hwnd, left, top, Width, Height: string): boolean;
var
  strData, strRet: string;
begin
  Result  := False;
  strData := setSendData(['setWindowPos', hwnd, left, top, Width, Height]);
  strret  := sendData(strdata);
  if strret = 'true' then
  begin
    Result := True;
  end;

end;

procedure TWindowsBot.moveMouse(const hwnd, x, y: string; mode: string; elementHwnd: string);
var
  strData: string;
begin

  strData := setSendData(['moveMouse', hwnd, x, y, mode, elementHwnd]);
  sendData(strdata);

end;

procedure TWindowsBot.moveMouseRelative(const hwnd, x, y: string; mode: string);
var
  strData: string;
begin

  strData := setSendData(['moveMouseRelative', hwnd, x, y, mode]);
  sendData(strdata);

end;

procedure TWindowsBot.rollMouse(const hwnd, x, y: string; dwData: string; mode: string);
var
  strData: string;
begin

  strData := setSendData(['rollMouse', hwnd, x, y, dwData, mode]);
  sendData(strdata);

end;

procedure TWindowsBot.clickMouse(const hwnd, x, y: string; msg: string; mode: string; elementHwnd: string);
var
  strData: string;
begin

  strData := setSendData(['clickMouse', hwnd, x, y, msg, mode, elementHwnd]);
  sendData(strdata);

end;

procedure TWindowsBot.sendKeys(const Text: string);
var
  strData: string;
begin

  strData := setSendData(['sendKeys', Text]);
  sendData(strdata);

end;

procedure TWindowsBot.sendVk(const bVk: string; msg: string);
var
  strData: string;
begin

  strData := setSendData(['sendVk', bVk, msg]);
  sendData(strdata);

end;

procedure TWindowsBot.sendVkByHwnd(const hwnd, bVk: string; msg: string);
var
  strData: string;
begin

  strData := setSendData(['sendKeysByHwnd', hwnd, bVk, msg]);
  sendData(strdata);

end;

function TWindowsBot.saveScreenshot(const hwnd, savePath: string; left: integer; top: integer; right: integer; bottom: integer; thresholdType: integer; thresh: integer; maxval: integer; mode: string): boolean;
var
  strData, strRet: string;
begin
  Result := False;
  if (thresholdType = 5) or (thresholdType = 6) then
  begin
    thresh := 127;
    maxval := 255;
  end;

  strData := setSendData(['saveScreenshot', hwnd, savePath, left.ToString, top.ToString, right.ToString, bottom.ToString, thresholdType.ToString, thresh.ToString, maxval.ToString, mode]);
  strret  := sendData(strdata);
  if strret = 'true' then
  begin
    Result := True;
  end;

end;

function TWindowsBot.getColor(const hwnd, x, y: string; mode: string): string;
var
  strData: string;
begin

  strData := setSendData(['getColor', hwnd, x, y, mode]);
  Result  := sendData(strdata);

end;

function TWindowsBot.findImage(const hwnd, imagePath: string; left: integer; top: integer; right: integer; bottom: integer; sim: string; thresholdType: integer; thresh: integer; maxval: integer; multi: integer; mode: string): string;
var
  strData: string;
begin

  strData := setSendData(['findImage', hwnd, imagePath, left.ToString, top.ToString, right.ToString, bottom.ToString, sim, thresholdType.ToString, thresh.ToString, maxval.ToString, multi.ToString, mode]);
  Result  := sendDataWaitTrue(strdata, '-1|-1');

end;

function TWindowsBot.findAnimation(const hwnd: string; frameRate: integer; left: integer; top: integer; right: integer; bottom: integer; mode: string): string;
var
  strData: string;
begin

  strData := setSendData(['findAnimation', hwnd, frameRate.ToString, left.ToString, top.ToString, right.ToString, bottom.ToString, mode]);
  Result  := sendDataWaitTrue(strdata, '-1|-1');

end;

function TWindowsBot.findColor(const hwnd, strMainColor: string; subColors: string; left: integer; top: integer; right: integer; bottom: integer; sim: string; thresholdType: integer; mode: string): string;
var
  strData: string;
begin

  strData := setSendData(['findColor', hwnd, strMainColor, subColors.Replace(',', #10), left.ToString, top.ToString, right.ToString, bottom.ToString, sim, mode]);
  Result  := sendDataWaitTrue(strdata, '-1|-1');

end;

function TWindowsBot.compareColor(const hwnd, mainX, mainY, strMainColor: string; subColors: string; left: integer; top: integer; right: integer; bottom: integer; sim: string; thresholdType: integer; mode: string): boolean;
var
  strData, strret: string;
begin

  strData := setSendData(['compareColor', hwnd, mainX, mainY, strMainColor, subColors.Replace(',', #10), left.ToString, top.ToString, right.ToString, bottom.ToString, sim, mode]);
  strret  := sendDataWaitTrue(strdata);
  Result  := strret = 'true';
end;

function TWindowsBot.ocr(const hwnd: string; left, top, right, bottom: integer; mode: string): string;
var
  strData: string;
begin

  strData := setSendData(['ocr', hwnd, left.ToString, top.ToString, right.ToString, bottom.ToString, mode]);
  Result  := sendData(strdata);

end;

function TWindowsBot.ocrByFile(const imagePath: string; left, top, right, bottom: integer): string;
var
  strData: string;
begin

  strData := setSendData(['ocrByFile', imagePath, left.ToString, top.ToString, right.ToString, bottom.ToString]);
  Result  := sendData(strdata);

end;

function TWindowsBot.getWords(const hwndOrImagePath: string; left: integer; top: integer; right: integer; bottom: integer; mode: string): string;
begin
  Result := '';

end;

function TWindowsBot.findWords(const hwndOrImagePath, words: string; left: integer; top: integer; right: integer; bottom: integer; mode: string): string;
begin
  Result := '';
end;

function TWindowsBot.getElementName(const hwnd, xpath: string): string;
var
  strData: string;
begin

  strData := setSendData(['getElementName', hwnd, xpath]);
  Result  := sendDataWaitTrue(strdata, 'null');

end;

function TWindowsBot.getElementValue(const hwnd, xpath: string): string;
var
  strData: string;
begin

  strData := setSendData(['getElementValue', hwnd, xpath]);
  Result  := sendDataWaitTrue(strdata, 'null');

end;

function TWindowsBot.getElementRect(const hwnd, xpath: string): string;
var
  strData: string;
begin

  strData := setSendData(['getElementRect', hwnd, xpath]);
  Result  := sendDataWaitTrue(strdata, '-1|-1|-1|-1');

end;

function TWindowsBot.getElementWindow(const hwnd, xpath: string): string;
var
  strData: string;
begin

  strData := setSendData(['getElementWindow', hwnd, xpath]);
  Result  := sendDataWaitTrue(strdata, 'null');

end;

function TWindowsBot.clickElement(const hwnd, xpath: string; msg: string): boolean;
var
  strData: string;
begin

  strData := setSendData(['clickElement', hwnd, xpath, msg]);
  Result  := sendDataWaitTrue(strdata) = 'true';

end;

function TWindowsBot.setElementFocus(const hwnd, xpath: string): boolean;
var
  strData: string;
begin

  strData := setSendData(['setElementFocus', hwnd, xpath]);
  Result  := sendDataWaitTrue(strdata) = 'true';

end;

function TWindowsBot.setElementValue(const hwnd, xpath, Value: string): boolean;
var
  strData: string;
begin

  strData := setSendData(['setElementValue', hwnd, xpath, Value]);
  Result  := sendDataWaitTrue(strdata) = 'true';

end;

function TWindowsBot.setElementScroll(const hwnd, xpath: string; horizontalPercent: integer; verticalPercent: integer): boolean;
var
  strData: string;
begin

  strData := setSendData(['setElementScroll', hwnd, xpath, horizontalPercent.ToString, verticalPercent.ToString]);
  Result  := sendDataWaitTrue(strdata) = 'true';

end;

function TWindowsBot.isSelected(const hwnd, xpath: string): boolean;
var
  strData: string;
begin

  strData := setSendData(['isSelected', hwnd, xpath]);
  Result  := sendDataWaitTrue(strdata) = 'selected';

end;

function TWindowsBot.closeWindow(const hwnd, xpath: string): boolean;
var
  strData: string;
begin

  strData := setSendData(['closeWindow', hwnd, xpath]);
  Result  := sendData(strdata) = 'true';

end;

function TWindowsBot.setWindowState(const hwnd, xpath: string; state: integer): boolean;
var
  strData: string;
begin

  strData := setSendData(['setWindowState', hwnd, xpath, state.ToString]);
  Result  := sendData(strdata) = 'true';

end;

function TWindowsBot.setClipboardText(const Text: string): boolean;
var
  strData: string;
begin

  strData := setSendData(['setClipboardText', Text]);
  Result  := sendData(strdata) = 'true';

end;

function TWindowsBot.getClipboardText: string;
var
  strData: string;
begin

  strData := setSendData(['getClipboardText']);
  Result  := sendData(strdata);

end;

function TWindowsBot.startProcess(const commandLine: string; showWindow: string; isWait: string): boolean;
var
  strData: string;
begin

  strData := setSendData(['startProcess', commandLine, showWindow, isWait]);
  Result  := sendData(strdata) = 'true';

end;

function TWindowsBot.executeCommand(const command: string; waitTimeout: integer): string;
var
  strData: string;
begin

  strData := setSendData(['executeCommand', command, waitTimeout.ToString]);
  Result  := sendData(strdata);

end;

procedure TWindowsBot.downloadFile(const url, filePath: string; isWait: string);
var
  strData: string;
begin

  strData := setSendData(['downloadFile', url, filePath, isWait]);
  sendData(strdata);

end;

function TWindowsBot.openExcel(const excelPath: string): string;
var
  strData: string;
begin

  strData := setSendData(['openExcel', excelPath]);
  Result  := sendData(strdata);

end;

function TWindowsBot.openExcelSheet(const excelObject, sheetName: string): string;
var
  strData: string;
  jData:   TJSONData;
begin

  jData   := GetJSON(excelObject);
  strData := setSendData(['openExcelSheet', jdata.FindPath('book').AsString, jdata.FindPath('path').AsString, sheetName]);
  Result  := sendData(strdata);

end;

function TWindowsBot.saveExcel(const excelObject: string): boolean;
var
  strData: string;
  jData:   TJSONData;
begin

  jData   := GetJSON(excelObject);
  strData := setSendData(['saveExcel', jdata.FindPath('book').AsString, jdata.FindPath('path').AsString]);
  Result  := sendData(strdata) = 'true';

end;

function TWindowsBot.writeExcelNum(const sheetObject: string; row, col, Value: integer): boolean;
var
  strData: string;
begin

  strData := setSendData(['writeExcelNum', sheetObject, row.ToString, col.ToString, Value.ToString]);
  Result  := sendData(strdata) = 'true';

end;

function TWindowsBot.writeExcelStr(const sheetObject: string; row, col: integer; strValue: string): boolean;
var
  strData: string;
begin

  strData := setSendData(['writeExcelStr', sheetObject, row.ToString, col.ToString, strValue]);
  Result  := sendData(strdata) = 'true';

end;

function TWindowsBot.readExcelNum(const sheetObject: string; row, col: integer): string;
var
  strData: string;
begin

  strData := setSendData(['readExcelNum', sheetObject, row.ToString, col.ToString]);
  Result  := sendData(strdata);

end;

function TWindowsBot.readExcelStr(const sheetObject: string; row, col: integer): string;
var
  strData: string;
begin

  strData := setSendData(['readExcelStr', sheetObject, row.ToString, col.ToString]);
  Result  := sendData(strdata);

end;

function TWindowsBot.removeExcelRow(const sheetObject: string; rowFirst, rowLast: integer): boolean;
var
  strData: string;
begin

  strData := setSendData(['removeExcelRow', sheetObject, rowFirst.ToString, rowLast.ToString]);
  Result  := sendData(strdata) = 'true';

end;

function TWindowsBot.removeExcelCol(const sheetObject: string; colFirst, colLast: integer): boolean;
var
  strData: string;
begin

  strData := setSendData(['removeExcelCol', sheetObject, colFirst.ToString, colLast.ToString]);
  Result  := sendData(strdata) = 'true';

end;

{$endregion}


{$region TWebBot }


constructor TWebBot.Create(pform: TForm; ip: string; port: integer; driverpath: string; browserName: string; userDataDir: string; browserPath: string; argument: string);
begin
  inherited Create(False);
  ParentForm := pform;


  waitTimeout     := 5000;
  intervalTimeout := 500;



  sock := TTCPBlockSocket.Create;
  sock.RaiseExcept := False;
  FreeOnTerminate := True;
  self.browserName := browserName;

  self.userDataDir := userDataDir;
  self.browserPath := browserPath;
  self.argument    := argument;
  self.Driverpath  := Driverpath;
  if (self.Driverpath = '') then
  begin
    self.Driverpath := 'WebDriver.exe';
  end;
  self.IP   := ip;
  self.Port := port;
end;

destructor TWebBot.Destroy;
begin
  inherited Destroy;
  try

    CheckAndKillChildProcess;

    if ClientSock <> nil then
    begin
      ClientSock.Free;
    end;
    sock.Free;
    if proc <> nil then
    begin
      Proc.Free;
    end;
  except
  end;

end;

procedure TWebBot.Execute;
var
  CSock: TSocket; //One per client connection, use array or list if multiple connection is possible
begin
  RunDriver;
  with sock do
  begin
    CreateSocket;
    setLinger(True, 10000);
    bind(IP, port.ToString);
    listen;

    repeat
      if terminated then break;
      if canread(1000) then
      begin
        CSock := accept;
        if lastError = 0 then
        begin
          ClientSock := TTCPBlockSocket.Create;
          ClientSock.socket := CSock;
          ClientSock.GetSins;

        end;
      end;
    until False;

    AbortSocket; // Free socket
  end;
end;

procedure TWebBot.RunDriver;
var
  parms: string;
begin

  try
    parms := Format('{"serverIp":"%s", "serverPort":%d, "browserName":"%s", "debugPort":%d, "userDataDir":"%s", "browserPath":"%s", "argument":"%s"}', [ip, port, browserName, 0, userDataDir, browserPath, argument], DefaultFormatSettings);

    parms := parms.Replace('"', '\"');

    Proc := tprocess.Create(ParentForm);
    proc.InheritHandles := True;

    Proc.Parameters.Clear;
    Proc.Executable := driverpath;
    Proc.Parameters.add('"' + parms + '"');

    Proc.Execute;

  except
  end;

end;

procedure TWebBot.CheckAndKillChildProcess;
const
  PROCESS_TERMINATE = $0001;
var
  Pn:    TProcesseNtry32;
  sHandle, sh: THandle;
  ParentProc: HWND;
  Found: boolean;
  //ExitCode: DWORD;
begin
  if proc = nil then exit;
  ParentProc := proc.ProcessID;

  sHandle := CreateToolHelp32SnapShot(TH32CS_SNAPALL, 0);
  try
    Pn.dwSize := sizeof(PROCESSENTRY32);
    Found     := Process32First(sHandle, Pn); //Search process
    while Found do //Loop all processes
    begin
      if Pn.th32ParentProcessID = ParentProc then
      begin

        sh := OpenProcess(PROCESS_TERMINATE, BOOL(0), Pn.th32ProcessID);
        try
          TerminateProcess(sh, 0);

        finally
          CloseHandle(sh);
        end;

      end;

      Found := Process32Next(sHandle, Pn); //Find next
    end;

  finally
    CloseHandle(sHandle);
  end;
end;

function TWebBot.IsConnected: boolean;
begin
  Result := (clientsock <> nil) and (clientsock.CanWrite(100));
end;

function TWebBot.setSendData(Args: array of string): string;
var
  i: integer;
  tempStr, strData, tstr: string;
begin
  Result  := '';
  strData := '';
  tempStr := '';
  for i := 0 to High(Args) do
  begin
    tstr    := Args[i];
    strData := strData + tencoding.UTF8.GetByteCount(UTF8Decode(tstr)).ToString + '/';
    tempStr := tempStr + tstr;

  end;
  strData := strdata + #10 + tempStr;
  Result  := strdata;

end;

function TWebBot.sendData(strData: string): string;
var
  tmpstr: string;
  //strlen: integer;
begin
  Result := '';
  if clientsock = nil then
  begin
    Result := '';
    exit;
  end;

  clientsock.SendString(UTF8Encode(strdata));
  tmpstr := clientsock.RecvPacket(5000);
  while (clientsock.CanRead(200)) do
  begin
    tmpstr := tmpstr + clientsock.RecvPacket(200);
    if clientsock.LastError = WSAETIMEDOUT then
      break;
  end;
  if tmpstr.Contains('/') then
  begin
    //strlen := StrToInt(tmpstr.Substring(0, tmpstr.IndexOf('/')));
    Result := tmpstr.Substring(tmpstr.IndexOf('/') + 1);
  end
  else
  begin
    Result := tmpstr;
  end;
end;

function TWebBot.sendDataWaitTrue(strData: string; waitret: string): string;
var
  dstart: tdatetime;
begin
  dstart := now;
  Result := sendData(strdata);
  while (Result = waitret) do
  begin
    sleep(intervalTimeout);
    Result := sendData(strdata);
    if (MilliSecondsBetween(now, dstart) > waitTimeout) then
    begin
      break;
    end;
  end;
end;

function TWebBot.gotoUrl(const url: string): boolean;
var
  strData, strRet: string;
begin
  Result  := False;
  strData := setSendData(['goto', url]);
  strret  := sendData(strdata);
  if strret = 'true' then
  begin
    Result := True;
  end;

end;

function TWebBot.newPage(const url: string): boolean;
var
  strData, strRet: string;
begin
  Result  := False;
  strData := setSendData(['newPage', url]);
  strret  := sendData(strdata);
  if strret = 'true' then
  begin
    Result := True;
  end;
end;

function TWebBot.back: boolean;
var
  strData, strRet: string;
begin
  Result  := False;
  strData := setSendData(['back']);
  strret  := sendData(strdata);
  if strret = 'true' then
  begin
    Result := True;
  end;
end;

function TWebBot.forward: boolean;
var
  strData, strRet: string;
begin
  Result  := False;
  strData := setSendData(['forward']);
  strret  := sendData(strdata);
  if strret = 'true' then
  begin
    Result := True;
  end;
end;

function TWebBot.refresh: boolean;
var
  strData, strRet: string;
begin
  Result  := False;
  strData := setSendData(['refresh']);
  strret  := sendData(strdata);
  if strret = 'true' then
  begin
    Result := True;
  end;
end;

function TWebBot.getCurPageId: string;
var
  strData: string;
begin

  strData := setSendData(['getCurPageId']);
  Result  := sendData(strdata);

end;

function TWebBot.getAllPageId: Tarray<string>;
var
  strData: string;
begin

  strData := setSendData(['getAllPageId']);
  Result  := sendData(strdata).Split('|');

end;

function TWebBot.switchPage(const pageId: string): boolean;
var
  strData, strRet: string;
begin
  Result  := False;
  strData := setSendData(['switchPage', pageId]);
  strret  := sendData(strdata);
  if strret = 'true' then
  begin
    Result := True;
  end;
end;

function TWebBot.closePage: boolean;
var
  strData, strRet: string;
begin
  Result  := False;
  strData := setSendData(['closePage']);
  strret  := sendData(strdata);
  if strret = 'true' then
  begin
    Result := True;
  end;

end;

function TWebBot.getCurrentUrl: string;
var
  strData: string;
begin

  strData := setSendData(['getCurrentUrl']);
  Result  := sendData(strdata);

end;

function TWebBot.getTitle: string;
var
  strData: string;
begin

  strData := setSendData(['getTitle']);
  Result  := sendData(strdata);

end;

function TWebBot.switchFrame(const xpath: string): boolean;
var
  strData, strRet: string;
begin
  Result  := False;
  strData := setSendData(['switchFrame', xpath]);
  strret  := sendDataWaitTrue(strdata);
  if strret = 'true' then
  begin
    Result := True;
  end;

end;

function TWebBot.switchMainFrame: boolean;
var
  strData, strRet: string;
begin
  Result  := False;
  strData := setSendData(['switchMainFrame']);
  strret  := sendData(strdata);
  if strret = 'true' then
  begin
    Result := True;
  end;

end;

function TWebBot.clickElement(const xpath: string): boolean;
var
  strData, strRet: string;
begin
  Result  := False;
  strData := setSendData(['clickElement', xpath]);
  strret  := sendDataWaitTrue(strdata);
  if strret = 'true' then
  begin
    Result := True;
  end;
end;

function TWebBot.setElementValue(const xpath, Value: string): boolean;
var
  strData, strRet: string;
begin
  Result  := False;
  strData := setSendData(['setElementValue', xpath, Value]);
  strret  := sendDataWaitTrue(strdata);
  if strret = 'true' then
  begin
    Result := True;
  end;

end;

function TWebBot.getElementText(const xpath: string): string;
var
  strData: string;
begin

  strData := setSendData(['getElementText', xpath]);
  Result  := sendDataWaitTrue(strdata, 'null');
end;

function TWebBot.getElementOuterHTML(const xpath: string): string;
var
  strData: string;
begin

  strData := setSendData(['getElementOuterHTML', xpath]);
  Result  := sendDataWaitTrue(strdata, 'null');

end;

function TWebBot.getElementInnerHTML(const xpath: string): string;
var
  strData: string;
begin

  strData := setSendData(['getElementInnerHTML', xpath]);
  Result  := sendDataWaitTrue(strdata, 'null');

end;

function TWebBot.setElementAttribute(const xpath, Name, Value: string): boolean;
var
  strData, strRet: string;
begin
  Result  := False;
  strData := setSendData(['setElementAttribute', xpath, Name, Value]);
  strret  := sendDataWaitTrue(strdata);
  if strret = 'true' then
  begin
    Result := True;
  end;
end;

function TWebBot.getElementAttribute(const xpath, Name: string): string;
var
  strData: string;
begin

  strData := setSendData(['getElementAttribute', xpath, Name]);
  Result  := sendDataWaitTrue(strdata, 'null');
end;

function TWebBot.getElementRect(const xpath: string): string;
var
  strData: string;
begin

  strData := setSendData(['getElementRect', xpath]);
  Result  := sendDataWaitTrue(strdata, 'null');

end;

function TWebBot.isSelected(const xpath: string): boolean;
var
  strData, strRet: string;
begin
  Result  := False;
  strData := setSendData(['isSelected', xpath]);
  strret  := sendDataWaitTrue(strdata, 'webdriver error');
  if strret = 'true' then
  begin
    Result := True;
  end;

end;

function TWebBot.isDisplayed(const xpath: string): boolean;
var
  strData, strRet: string;
begin
  Result  := False;
  strData := setSendData(['isDisplayed', xpath]);
  strret  := sendDataWaitTrue(strdata, 'webdriver error');
  if strret = 'true' then
  begin
    Result := True;
  end;

end;

function TWebBot.isEnabled(const xpath: string): boolean;
var
  strData, strRet: string;
begin
  Result  := False;
  strData := setSendData(['isEnabled', xpath]);
  strret  := sendDataWaitTrue(strdata, 'webdriver error');
  if strret = 'true' then
  begin
    Result := True;
  end;

end;

function TWebBot.clearElement(const xpath: string): boolean;
var
  strData, strRet: string;
begin
  Result  := False;
  strData := setSendData(['clearElement', xpath]);
  strret  := sendDataWaitTrue(strdata);
  if strret = 'true' then
  begin
    Result := True;
  end;

end;

function TWebBot.setElementFocus(const xpath: string): boolean;
var
  strData, strRet: string;
begin
  Result  := False;
  strData := setSendData(['setElementFocus', xpath]);
  strret  := sendDataWaitTrue(strdata);
  if strret = 'true' then
  begin
    Result := True;
  end;

end;

function TWebBot.uploadFile(const xpath, filePath: string): boolean;
var
  strData, strRet: string;
begin
  Result  := False;
  strData := setSendData(['uploadFile', xpath, filePath]);
  strret  := sendDataWaitTrue(strdata);
  if strret = 'true' then
  begin
    Result := True;
  end;

end;

function TWebBot.sendKeys(const xpath, Value: string): boolean;
var
  strData, strRet: string;
begin
  Result  := False;
  strData := setSendData(['sendKeys', xpath, Value]);
  strret  := sendDataWaitTrue(strdata);
  if strret = 'true' then
  begin
    Result := True;
  end;
end;

function TWebBot.sendVk(const vkCode: string): boolean;
var
  strData, strRet: string;
begin
  Result  := False;
  strData := setSendData(['sendVk', vkCode]);
  strret  := sendData(strdata);
  if strret = 'true' then
  begin
    Result := True;
  end;

end;

function TWebBot.clickMouse(x, y, msg: integer): boolean;
var
  strData, strRet: string;
begin
  Result  := False;
  strData := setSendData(['clickMouse', x.ToString, y.ToString, msg.ToString]);
  strret  := sendData(strdata);
  if strret = 'true' then
  begin
    Result := True;
  end;

end;

function TWebBot.moveMouse(x, y: integer): boolean;
var
  strData, strRet: string;
begin
  Result  := False;
  strData := setSendData(['moveMouse', x.ToString, y.ToString]);
  strret  := sendData(strdata);
  if strret = 'true' then
  begin
    Result := True;
  end;

end;

function TWebBot.wheelMouse(deltaX, deltaY: integer; x: integer; y: integer): boolean;
var
  strData, strRet: string;
begin
  Result  := False;
  strData := setSendData(['wheelMouse', deltaX.ToString, deltaY.ToString, x.ToString, y.ToString]);
  strret  := sendData(strdata);
  if strret = 'true' then
  begin
    Result := True;
  end;

end;

function TWebBot.clickMouseByXpath(const xpath: string; msg: integer): boolean;
var
  strData, strRet: string;
begin
  Result  := False;
  strData := setSendData(['clickMouseByXpath', xpath, msg.ToString]);
  strret  := sendData(strdata);
  if strret = 'true' then
  begin
    Result := True;
  end;

end;

function TWebBot.moveMouseByXpath(const xpath: string): boolean;
var
  strData, strRet: string;
begin
  Result  := False;
  strData := setSendData(['moveMouseByXpath', xpath]);
  strret  := sendData(strdata);
  if strret = 'true' then
  begin
    Result := True;
  end;

end;

function TWebBot.wheelMouseByXpath(const xpath: string; deltaX, deltaY: integer): boolean;
var
  strData, strRet: string;
begin
  Result  := False;
  strData := setSendData(['wheelMouseByXpath', xpath, deltaX.ToString, deltaY.ToString]);
  strret  := sendData(strdata);
  if strret = 'true' then
  begin
    Result := True;
  end;

end;

function TWebBot.takeScreenshot(xpath: string): string;
var
  strData: string;
begin
  if xpath = '' then
  begin
    strData := setSendData(['takeScreenshot']);
  end
  else
  begin
    strData := setSendData(['takeScreenshot', xpath]);
  end;

  Result := sendDataWaitTrue(strdata, 'null');

end;

function TWebBot.clickAlert(const acceptOrCancel: string; promptText: string): boolean;
var
  strData, strRet: string;
begin
  Result  := False;
  strData := setSendData(['clickAlert', acceptOrCancel, promptText]);
  strret  := sendData(strdata);
  if strret = 'true' then
  begin
    Result := True;
  end;

end;

function TWebBot.getAlertText: string;
var
  strData: string;
begin

  strData := setSendData(['getAlertText']);
  Result  := sendData(strdata);

end;

function TWebBot.getCookies(const url: string): string;
var
  strData: string;
begin

  strData := setSendData(['getCookies', url]);
  Result  := sendData(strdata);

end;

function TWebBot.getAllCookies: string;
var
  strData: string;
begin

  strData := setSendData(['getAllCookies']);
  Result  := sendData(strdata);

end;

function TWebBot.setCookie(const Name, Value, url: string; domain: string; path: string; secure: string; httpOnly: string; sameSite: string; expires: string; priority: string; sameParty: string;
  sourceScheme: string; sourcePort: string; partitionKey: string): boolean;
var
  strData, strRet: string;
begin
  Result  := False;
  strData := setSendData(['setCookie', Name, Value, url, domain, path, secure, httpOnly, sameSite, expires, priority, sameParty, sourceScheme, sourcePort, partitionKey]);
  strret  := sendData(strdata);
  if strret = 'true' then
  begin
    Result := True;
  end;

end;

function TWebBot.deleteCookies(const Name: string; url: string; domain: string; path: string): boolean;
var
  strData, strRet: string;
begin
  Result  := False;
  strData := setSendData(['deleteCookies', Name, url, domain, path]);
  strret  := sendData(strdata);
  if strret = 'true' then
  begin
    Result := True;
  end;

end;

function TWebBot.deleteAllCookies: boolean;
var
  strData, strRet: string;
begin
  Result  := False;
  strData := setSendData(['deleteAllCookies']);
  strret  := sendData(strdata);
  if strret = 'true' then
  begin
    Result := True;
  end;

end;

function TWebBot.clearCache: boolean;
var
  strData, strRet: string;
begin
  Result  := False;
  strData := setSendData(['clearCache']);
  strret  := sendData(strdata);
  if strret = 'true' then
  begin
    Result := True;
  end;

end;

function TWebBot.executeScript(const command: string): string;
var
  strData: string;
begin
  strData := setSendData(['executeScript', command]);
  Result  := sendData(strdata);

end;

function TWebBot.getWindowPos: string;
var
  strData: string;
begin
  strData := setSendData(['getWindowPos']);
  Result  := sendData(strdata);

end;

function TWebBot.setWindowPos(const windowState: string; left: integer; top: integer; Width: integer; Height: integer): boolean;
var
  strData, strRet: string;
begin
  Result  := False;
  strData := setSendData(['setWindowPos', windowState, left.ToString, top.ToString, Width.ToString, Height.ToString]);
  strret  := sendData(strdata);
  if strret = 'true' then
  begin
    Result := True;
  end;

end;

{$endregion}

end.
